diff options
author | Federico Ceratto <federico.ceratto@gmail.com> | 2017-04-30 16:09:37 +0000 |
---|---|---|
committer | Federico Ceratto <federico.ceratto@gmail.com> | 2017-04-30 16:09:37 +0000 |
commit | 51f689a8e17ff3929acd2dbf39e936d2cd3ac723 (patch) | |
tree | 92e54f543171b69dcbc639be09d11221cf96ba28 /src | |
parent | New upstream version 1.5.0+dfsg (diff) | |
download | netdata-51f689a8e17ff3929acd2dbf39e936d2cd3ac723.tar.xz netdata-51f689a8e17ff3929acd2dbf39e936d2cd3ac723.zip |
New upstream version 1.6.0+dfsgupstream/1.6.0+dfsg
Diffstat (limited to 'src')
111 files changed, 22605 insertions, 12919 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d8274d255..1c1dd3385 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,11 +12,14 @@ AM_CPPFLAGS = \ -DRUN_DIR="\"$(localstatedir)/run/netdata\"" \ -DWEB_DIR="\"$(webdir)\"" \ $(NULL) + AM_CFLAGS = \ $(OPTIONAL_MATH_CFLAGS) \ $(OPTIONAL_NFACCT_CLFAGS) \ $(OPTIONAL_ZLIB_CFLAGS) \ $(OPTIONAL_UUID_CFLAGS) \ + $(OPTIONAL_LIBCAP_CFLAGS) \ + $(OPTIONAL_IPMIMONITORING_CFLAGS)\ $(NULL) sbin_PROGRAMS = netdata @@ -24,66 +27,131 @@ dist_cache_DATA = .keep dist_varlib_DATA = .keep dist_registry_DATA = .keep dist_log_DATA = .keep -if !MACOS -plugins_PROGRAMS = apps.plugin +plugins_PROGRAMS = + +if ENABLE_PLUGIN_APPS +plugins_PROGRAMS += apps.plugin +endif + +if ENABLE_PLUGIN_FREEIPMI +plugins_PROGRAMS += freeipmi.plugin endif netdata_SOURCES = \ - appconfig.c appconfig.h \ - adaptive_resortable_list.c adaptive_resortable_list.h \ - avl.c avl.h \ - backends.c backends.h \ - clocks.c clocks.h \ - 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 \ + adaptive_resortable_list.c \ + adaptive_resortable_list.h \ + appconfig.c \ + appconfig.h \ + avl.c \ + avl.h \ + backends.c \ + backends.h \ + clocks.c \ + clocks.h \ + 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 \ + health_config.c \ + health_json.c \ + health_log.c \ inlined.h \ - log.c log.h \ - main.c main.h \ - plugin_checks.c plugin_checks.h \ - plugin_idlejitter.c plugin_idlejitter.h \ - plugin_nfacct.c plugin_nfacct.h \ - plugin_tc.c plugin_tc.h \ - plugins_d.c plugins_d.h \ - popen.c popen.h \ - socket.c socket.h \ - simple_pattern.c simple_pattern.h \ - sys_fs_cgroup.c \ - sys_devices_system_edac_mc.c \ - sys_devices_system_node.c \ - procfile.c procfile.h \ - proc_self_mountinfo.c proc_self_mountinfo.h \ - registry.c registry.h \ - registry_internals.c registry_internals.h \ - registry_url.c registry_url.h \ - registry_person.c registry_person.h \ - registry_machine.c registry_machine.h \ - registry_init.c \ + locks.h \ + log.c \ + log.h \ + main.c \ + main.h \ + plugin_checks.c \ + plugin_checks.h \ + plugin_idlejitter.c \ + plugin_idlejitter.h \ + plugin_nfacct.c \ + plugin_nfacct.h \ + plugin_tc.c \ + plugin_tc.h \ + plugins_d.c \ + plugins_d.h \ + popen.c \ + popen.h \ + proc_self_mountinfo.c \ + proc_self_mountinfo.h \ + procfile.c \ + procfile.h \ + registry.c \ + registry.h \ registry_db.c \ + registry_init.c \ + registry_internals.c \ + registry_internals.h \ registry_log.c \ - rrd.c rrd.h \ - rrd2json.c rrd2json.h \ - storage_number.c storage_number.h \ - unit_test.c unit_test.h \ + registry_machine.c \ + registry_machine.h \ + registry_person.c \ + registry_person.h \ + registry_url.c \ + registry_url.h \ + rrd.c \ + rrd.h \ + rrd2json.c \ + rrd2json.h \ + rrd2json_api_old.c \ + rrd2json_api_old.h \ + rrdcalc.c \ + rrdcalctemplate.c \ + rrddim.c \ + rrddimvar.c \ + rrdfamily.c \ + rrdhost.c \ + rrdpush.c \ + rrdpush.h \ + rrdset.c \ + rrdsetvar.c \ + rrdvar.c \ + simple_pattern.c \ + simple_pattern.h \ + socket.c \ + socket.h \ + storage_number.c \ + storage_number.h \ + sys_devices_system_edac_mc.c \ + sys_devices_system_node.c \ + sys_fs_cgroup.c \ + 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 \ + web_api_old.c \ + web_api_old.h \ + web_api_v1.c \ + web_api_v1.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) if FREEBSD netdata_SOURCES += \ - plugin_freebsd.c plugin_freebsd.h \ + plugin_freebsd.c \ + plugin_freebsd.h \ freebsd_sysctl.c \ $(NULL) else if MACOS netdata_SOURCES += \ - plugin_macos.c plugin_macos.h \ + plugin_macos.c \ + plugin_macos.h \ macos_sysctl.c \ macos_mach_smi.c \ macos_fw.c \ @@ -91,8 +159,10 @@ netdata_SOURCES += \ else netdata_SOURCES += \ ipc.c ipc.h \ - plugin_proc.c plugin_proc.h \ - plugin_proc_diskspace.c plugin_proc_diskspace.h \ + plugin_proc.c \ + plugin_proc.h \ + plugin_proc_diskspace.c \ + plugin_proc_diskspace.h \ proc_diskstats.c \ proc_interrupts.c \ proc_softirqs.c \ @@ -135,19 +205,26 @@ apps_plugin_SOURCES = \ web_buffer.c web_buffer.h \ $(NULL) -install-data-hook: - if [ `id -u` == 0 ]; then \ - chown root '$(DESTDIR)$(pluginsdir)/apps.plugin' && \ - chmod 0755 '$(DESTDIR)$(pluginsdir)/apps.plugin' && \ - ( setcap cap_dac_read_search,cap_sys_ptrace+ep '$(DESTDIR)$(pluginsdir)/apps.plugin' || \ - chmod 4755 '$(DESTDIR)$(pluginsdir)/apps.plugin' ); \ - else \ - echo; \ - echo "ATTENTION"; \ - echo; \ - echo "$(pluginsdir)/apps.plugin requires escalated capabilities:"; \ - echo "sudo chown root '$(DESTDIR)$(pluginsdir)/apps.plugin'"; \ - echo "sudo chmod 0755 '$(DESTDIR)$(pluginsdir)/apps.plugin'"; \ - echo "sudo setcap cap_dac_read_search,cap_sys_ptrace+ep '$(DESTDIR)$(pluginsdir)/apps.plugin'"; \ - echo; \ - fi +if FREEBSD +apps_plugin_SOURCES += \ + plugin_freebsd.h \ + $(NULL) +endif + +apps_plugin_LDADD = \ + $(OPTIONAL_MATH_LIBS) \ + $(OPTIONAL_LIBCAP_LIBS) \ + $(NULL) + +freeipmi_plugin_SOURCES = \ + freeipmi_plugin.c \ + clocks.c clocks.h \ + common.c common.h \ + inlined.h \ + log.c log.h \ + procfile.c procfile.h \ + $(NULL) + +freeipmi_plugin_LDADD = \ + $(OPTIONAL_IPMIMONITORING_LIBS) \ + $(NULL) diff --git a/src/Makefile.in b/src/Makefile.in index f94646363..d7229d18a 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,8 +1,9 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.11.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. - +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -16,61 +17,6 @@ VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -90,23 +36,29 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ sbin_PROGRAMS = netdata$(EXEEXT) -@MACOS_FALSE@plugins_PROGRAMS = apps.plugin$(EXEEXT) -@FREEBSD_TRUE@am__append_1 = \ -@FREEBSD_TRUE@ plugin_freebsd.c plugin_freebsd.h \ +plugins_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) +@ENABLE_PLUGIN_APPS_TRUE@am__append_1 = apps.plugin +@ENABLE_PLUGIN_FREEIPMI_TRUE@am__append_2 = freeipmi.plugin +@FREEBSD_TRUE@am__append_3 = \ +@FREEBSD_TRUE@ plugin_freebsd.c \ +@FREEBSD_TRUE@ plugin_freebsd.h \ @FREEBSD_TRUE@ freebsd_sysctl.c \ @FREEBSD_TRUE@ $(NULL) -@FREEBSD_FALSE@@MACOS_TRUE@am__append_2 = \ -@FREEBSD_FALSE@@MACOS_TRUE@ plugin_macos.c plugin_macos.h \ +@FREEBSD_FALSE@@MACOS_TRUE@am__append_4 = \ +@FREEBSD_FALSE@@MACOS_TRUE@ plugin_macos.c \ +@FREEBSD_FALSE@@MACOS_TRUE@ plugin_macos.h \ @FREEBSD_FALSE@@MACOS_TRUE@ macos_sysctl.c \ @FREEBSD_FALSE@@MACOS_TRUE@ macos_mach_smi.c \ @FREEBSD_FALSE@@MACOS_TRUE@ macos_fw.c \ @FREEBSD_FALSE@@MACOS_TRUE@ $(NULL) -@FREEBSD_FALSE@@MACOS_FALSE@am__append_3 = \ +@FREEBSD_FALSE@@MACOS_FALSE@am__append_5 = \ @FREEBSD_FALSE@@MACOS_FALSE@ ipc.c ipc.h \ -@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc.c plugin_proc.h \ -@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc_diskspace.c plugin_proc_diskspace.h \ +@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc.h \ +@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc_diskspace.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc_diskspace.h \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_diskstats.c \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_interrupts.c \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_softirqs.c \ @@ -129,10 +81,17 @@ sbin_PROGRAMS = netdata$(EXEEXT) @FREEBSD_FALSE@@MACOS_FALSE@ sys_kernel_mm_ksm.c \ @FREEBSD_FALSE@@MACOS_FALSE@ $(NULL) +@FREEBSD_TRUE@am__append_6 = \ +@FREEBSD_TRUE@ plugin_freebsd.h \ +@FREEBSD_TRUE@ $(NULL) + subdir = src +DIST_COMMON = $(dist_cache_DATA) $(dist_log_DATA) \ + $(dist_registry_DATA) $(dist_varlib_DATA) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ - $(top_srcdir)/m4/ax_c__generic.m4 \ + $(top_srcdir)/m4/ax_c__generic.m4 $(top_srcdir)/m4/ax_c_lto.m4 \ $(top_srcdir)/m4/ax_c_mallinfo.m4 \ $(top_srcdir)/m4/ax_c_mallopt.m4 \ $(top_srcdir)/m4/ax_check_compile_flag.m4 \ @@ -141,43 +100,56 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(dist_cache_DATA) \ - $(dist_log_DATA) $(dist_registry_DATA) $(dist_varlib_DATA) \ - $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +@ENABLE_PLUGIN_APPS_TRUE@am__EXEEXT_1 = apps.plugin$(EXEEXT) +@ENABLE_PLUGIN_FREEIPMI_TRUE@am__EXEEXT_2 = freeipmi.plugin$(EXEEXT) am__installdirs = "$(DESTDIR)$(pluginsdir)" "$(DESTDIR)$(sbindir)" \ "$(DESTDIR)$(cachedir)" "$(DESTDIR)$(logdir)" \ "$(DESTDIR)$(registrydir)" "$(DESTDIR)$(varlibdir)" PROGRAMS = $(plugins_PROGRAMS) $(sbin_PROGRAMS) +am__apps_plugin_SOURCES_DIST = apps_plugin.c avl.c avl.h clocks.c \ + clocks.h common.c common.h inlined.h log.c log.h procfile.c \ + procfile.h web_buffer.c web_buffer.h plugin_freebsd.h +am__objects_1 = am_apps_plugin_OBJECTS = apps_plugin.$(OBJEXT) avl.$(OBJEXT) \ clocks.$(OBJEXT) common.$(OBJEXT) log.$(OBJEXT) \ - procfile.$(OBJEXT) web_buffer.$(OBJEXT) + procfile.$(OBJEXT) web_buffer.$(OBJEXT) $(am__objects_1) apps_plugin_OBJECTS = $(am_apps_plugin_OBJECTS) -apps_plugin_LDADD = $(LDADD) -am__netdata_SOURCES_DIST = appconfig.c appconfig.h \ - adaptive_resortable_list.c adaptive_resortable_list.h avl.c \ - avl.h backends.c backends.h clocks.c clocks.h 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 inlined.h log.c log.h main.c main.h plugin_checks.c \ - plugin_checks.h plugin_idlejitter.c plugin_idlejitter.h \ - plugin_nfacct.c plugin_nfacct.h plugin_tc.c plugin_tc.h \ - plugins_d.c plugins_d.h popen.c popen.h socket.c socket.h \ - simple_pattern.c simple_pattern.h sys_fs_cgroup.c \ - sys_devices_system_edac_mc.c sys_devices_system_node.c \ - procfile.c procfile.h proc_self_mountinfo.c \ - proc_self_mountinfo.h registry.c registry.h \ - registry_internals.c registry_internals.h registry_url.c \ - registry_url.h registry_person.c registry_person.h \ - registry_machine.c registry_machine.h registry_init.c \ - registry_db.c registry_log.c rrd.c rrd.h rrd2json.c rrd2json.h \ - storage_number.c storage_number.h 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 plugin_freebsd.c plugin_freebsd.h \ +am__DEPENDENCIES_1 = +apps_plugin_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_freeipmi_plugin_OBJECTS = freeipmi_plugin.$(OBJEXT) \ + clocks.$(OBJEXT) common.$(OBJEXT) log.$(OBJEXT) \ + procfile.$(OBJEXT) +freeipmi_plugin_OBJECTS = $(am_freeipmi_plugin_OBJECTS) +freeipmi_plugin_DEPENDENCIES = $(am__DEPENDENCIES_1) +am__netdata_SOURCES_DIST = adaptive_resortable_list.c \ + adaptive_resortable_list.h appconfig.c appconfig.h avl.c avl.h \ + backends.c backends.h clocks.c clocks.h 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 \ + health_config.c health_json.c health_log.c inlined.h locks.h \ + log.c log.h main.c main.h plugin_checks.c plugin_checks.h \ + plugin_idlejitter.c plugin_idlejitter.h plugin_nfacct.c \ + plugin_nfacct.h plugin_tc.c plugin_tc.h plugins_d.c \ + plugins_d.h popen.c popen.h proc_self_mountinfo.c \ + proc_self_mountinfo.h procfile.c procfile.h registry.c \ + registry.h registry_db.c registry_init.c registry_internals.c \ + registry_internals.h registry_log.c registry_machine.c \ + registry_machine.h registry_person.c registry_person.h \ + registry_url.c registry_url.h rrd.c rrd.h rrd2json.c \ + rrd2json.h rrd2json_api_old.c rrd2json_api_old.h rrdcalc.c \ + rrdcalctemplate.c rrddim.c rrddimvar.c rrdfamily.c rrdhost.c \ + rrdpush.c rrdpush.h rrdset.c rrdsetvar.c rrdvar.c \ + simple_pattern.c simple_pattern.h socket.c socket.h \ + storage_number.c storage_number.h sys_devices_system_edac_mc.c \ + sys_devices_system_node.c sys_fs_cgroup.c unit_test.c \ + unit_test.h url.c url.h web_api_old.c web_api_old.h \ + web_api_v1.c web_api_v1.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 plugin_freebsd.c plugin_freebsd.h \ freebsd_sysctl.c plugin_macos.c plugin_macos.h macos_sysctl.c \ macos_mach_smi.c macos_fw.c ipc.c ipc.h plugin_proc.c \ plugin_proc.h plugin_proc_diskspace.c plugin_proc_diskspace.h \ @@ -189,13 +161,13 @@ am__netdata_SOURCES_DIST = appconfig.c appconfig.h \ proc_net_stat_synproxy.c proc_stat.c \ proc_sys_kernel_random_entropy_avail.c proc_vmstat.c \ proc_uptime.c sys_kernel_mm_ksm.c -@FREEBSD_TRUE@am__objects_1 = plugin_freebsd.$(OBJEXT) \ +@FREEBSD_TRUE@am__objects_2 = plugin_freebsd.$(OBJEXT) \ @FREEBSD_TRUE@ freebsd_sysctl.$(OBJEXT) -@FREEBSD_FALSE@@MACOS_TRUE@am__objects_2 = plugin_macos.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_TRUE@am__objects_3 = plugin_macos.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_TRUE@ macos_sysctl.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_TRUE@ macos_mach_smi.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_TRUE@ macos_fw.$(OBJEXT) -@FREEBSD_FALSE@@MACOS_FALSE@am__objects_3 = ipc.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@am__objects_4 = ipc.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_FALSE@ plugin_proc_diskspace.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_diskstats.$(OBJEXT) \ @@ -218,65 +190,48 @@ am__netdata_SOURCES_DIST = appconfig.c appconfig.h \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_vmstat.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_uptime.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_FALSE@ sys_kernel_mm_ksm.$(OBJEXT) -am_netdata_OBJECTS = appconfig.$(OBJEXT) \ - adaptive_resortable_list.$(OBJEXT) avl.$(OBJEXT) \ - backends.$(OBJEXT) clocks.$(OBJEXT) common.$(OBJEXT) \ - daemon.$(OBJEXT) dictionary.$(OBJEXT) eval.$(OBJEXT) \ - global_statistics.$(OBJEXT) health.$(OBJEXT) log.$(OBJEXT) \ - main.$(OBJEXT) plugin_checks.$(OBJEXT) \ - plugin_idlejitter.$(OBJEXT) plugin_nfacct.$(OBJEXT) \ - plugin_tc.$(OBJEXT) plugins_d.$(OBJEXT) popen.$(OBJEXT) \ - socket.$(OBJEXT) simple_pattern.$(OBJEXT) \ - sys_fs_cgroup.$(OBJEXT) sys_devices_system_edac_mc.$(OBJEXT) \ - sys_devices_system_node.$(OBJEXT) procfile.$(OBJEXT) \ - proc_self_mountinfo.$(OBJEXT) registry.$(OBJEXT) \ - registry_internals.$(OBJEXT) registry_url.$(OBJEXT) \ - registry_person.$(OBJEXT) registry_machine.$(OBJEXT) \ - registry_init.$(OBJEXT) registry_db.$(OBJEXT) \ - registry_log.$(OBJEXT) rrd.$(OBJEXT) rrd2json.$(OBJEXT) \ - storage_number.$(OBJEXT) unit_test.$(OBJEXT) url.$(OBJEXT) \ - web_buffer.$(OBJEXT) web_buffer_svg.$(OBJEXT) \ - web_client.$(OBJEXT) web_server.$(OBJEXT) $(am__objects_1) \ - $(am__objects_2) $(am__objects_3) +am_netdata_OBJECTS = adaptive_resortable_list.$(OBJEXT) \ + appconfig.$(OBJEXT) avl.$(OBJEXT) backends.$(OBJEXT) \ + clocks.$(OBJEXT) common.$(OBJEXT) daemon.$(OBJEXT) \ + dictionary.$(OBJEXT) eval.$(OBJEXT) \ + global_statistics.$(OBJEXT) health.$(OBJEXT) \ + health_config.$(OBJEXT) health_json.$(OBJEXT) \ + health_log.$(OBJEXT) log.$(OBJEXT) main.$(OBJEXT) \ + plugin_checks.$(OBJEXT) plugin_idlejitter.$(OBJEXT) \ + plugin_nfacct.$(OBJEXT) plugin_tc.$(OBJEXT) \ + plugins_d.$(OBJEXT) popen.$(OBJEXT) \ + proc_self_mountinfo.$(OBJEXT) procfile.$(OBJEXT) \ + registry.$(OBJEXT) registry_db.$(OBJEXT) \ + registry_init.$(OBJEXT) registry_internals.$(OBJEXT) \ + registry_log.$(OBJEXT) registry_machine.$(OBJEXT) \ + registry_person.$(OBJEXT) registry_url.$(OBJEXT) rrd.$(OBJEXT) \ + rrd2json.$(OBJEXT) rrd2json_api_old.$(OBJEXT) \ + rrdcalc.$(OBJEXT) rrdcalctemplate.$(OBJEXT) rrddim.$(OBJEXT) \ + rrddimvar.$(OBJEXT) rrdfamily.$(OBJEXT) rrdhost.$(OBJEXT) \ + rrdpush.$(OBJEXT) rrdset.$(OBJEXT) rrdsetvar.$(OBJEXT) \ + rrdvar.$(OBJEXT) simple_pattern.$(OBJEXT) socket.$(OBJEXT) \ + storage_number.$(OBJEXT) sys_devices_system_edac_mc.$(OBJEXT) \ + sys_devices_system_node.$(OBJEXT) sys_fs_cgroup.$(OBJEXT) \ + unit_test.$(OBJEXT) url.$(OBJEXT) web_api_old.$(OBJEXT) \ + web_api_v1.$(OBJEXT) web_buffer.$(OBJEXT) \ + web_buffer_svg.$(OBJEXT) web_client.$(OBJEXT) \ + web_server.$(OBJEXT) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) netdata_OBJECTS = $(am_netdata_OBJECTS) -am__DEPENDENCIES_1 = netdata_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -SOURCES = $(apps_plugin_SOURCES) $(netdata_SOURCES) -DIST_SOURCES = $(apps_plugin_SOURCES) $(am__netdata_SOURCES_DIST) -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac +SOURCES = $(apps_plugin_SOURCES) $(freeipmi_plugin_SOURCES) \ + $(netdata_SOURCES) +DIST_SOURCES = $(am__apps_plugin_SOURCES_DIST) \ + $(freeipmi_plugin_SOURCES) $(am__netdata_SOURCES_DIST) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -306,30 +261,11 @@ am__uninstall_files_from_dir = { \ } 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 -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags -am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -353,7 +289,11 @@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPMIMONITORING_CFLAGS = @IPMIMONITORING_CFLAGS@ +IPMIMONITORING_LIBS = @IPMIMONITORING_LIBS@ LDFLAGS = @LDFLAGS@ +LIBCAP_CFLAGS = @LIBCAP_CFLAGS@ +LIBCAP_LIBS = @LIBCAP_LIBS@ LIBMNL_CFLAGS = @LIBMNL_CFLAGS@ LIBMNL_LIBS = @LIBMNL_LIBS@ LIBOBJS = @LIBOBJS@ @@ -367,6 +307,10 @@ MKDIR_P = @MKDIR_P@ NFACCT_CFLAGS = @NFACCT_CFLAGS@ NFACCT_LIBS = @NFACCT_LIBS@ OBJEXT = @OBJEXT@ +OPTIONAL_IPMIMONITORING_CFLAGS = @OPTIONAL_IPMIMONITORING_CFLAGS@ +OPTIONAL_IPMIMONITORING_LIBS = @OPTIONAL_IPMIMONITORING_LIBS@ +OPTIONAL_LIBCAP_CFLAGS = @OPTIONAL_LIBCAP_CFLAGS@ +OPTIONAL_LIBCAP_LIBS = @OPTIONAL_LIBCAP_LIBS@ OPTIONAL_MATH_CLFAGS = @OPTIONAL_MATH_CLFAGS@ OPTIONAL_MATH_LIBS = @OPTIONAL_MATH_LIBS@ OPTIONAL_NFACCT_CLFAGS = @OPTIONAL_NFACCT_CLFAGS@ @@ -483,33 +427,40 @@ AM_CFLAGS = \ $(OPTIONAL_NFACCT_CLFAGS) \ $(OPTIONAL_ZLIB_CFLAGS) \ $(OPTIONAL_UUID_CFLAGS) \ + $(OPTIONAL_LIBCAP_CFLAGS) \ + $(OPTIONAL_IPMIMONITORING_CFLAGS)\ $(NULL) dist_cache_DATA = .keep dist_varlib_DATA = .keep dist_registry_DATA = .keep dist_log_DATA = .keep -netdata_SOURCES = appconfig.c appconfig.h adaptive_resortable_list.c \ - adaptive_resortable_list.h avl.c avl.h backends.c backends.h \ - clocks.c clocks.h 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 inlined.h log.c log.h \ - main.c main.h plugin_checks.c plugin_checks.h \ +netdata_SOURCES = adaptive_resortable_list.c \ + adaptive_resortable_list.h appconfig.c appconfig.h avl.c avl.h \ + backends.c backends.h clocks.c clocks.h 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 \ + health_config.c health_json.c health_log.c inlined.h locks.h \ + log.c log.h main.c main.h plugin_checks.c plugin_checks.h \ plugin_idlejitter.c plugin_idlejitter.h plugin_nfacct.c \ plugin_nfacct.h plugin_tc.c plugin_tc.h plugins_d.c \ - plugins_d.h popen.c popen.h socket.c socket.h simple_pattern.c \ - simple_pattern.h sys_fs_cgroup.c sys_devices_system_edac_mc.c \ - sys_devices_system_node.c procfile.c procfile.h \ - proc_self_mountinfo.c proc_self_mountinfo.h registry.c \ - registry.h registry_internals.c registry_internals.h \ - registry_url.c registry_url.h registry_person.c \ - registry_person.h registry_machine.c registry_machine.h \ - registry_init.c registry_db.c registry_log.c rrd.c rrd.h \ - rrd2json.c rrd2json.h storage_number.c storage_number.h \ - unit_test.c unit_test.h url.c url.h web_buffer.c web_buffer.h \ + plugins_d.h popen.c popen.h proc_self_mountinfo.c \ + proc_self_mountinfo.h procfile.c procfile.h registry.c \ + registry.h registry_db.c registry_init.c registry_internals.c \ + registry_internals.h registry_log.c registry_machine.c \ + registry_machine.h registry_person.c registry_person.h \ + registry_url.c registry_url.h rrd.c rrd.h rrd2json.c \ + rrd2json.h rrd2json_api_old.c rrd2json_api_old.h rrdcalc.c \ + rrdcalctemplate.c rrddim.c rrddimvar.c rrdfamily.c rrdhost.c \ + rrdpush.c rrdpush.h rrdset.c rrdsetvar.c rrdvar.c \ + simple_pattern.c simple_pattern.h socket.c socket.h \ + storage_number.c storage_number.h sys_devices_system_edac_mc.c \ + sys_devices_system_node.c sys_fs_cgroup.c unit_test.c \ + unit_test.h url.c url.h web_api_old.c web_api_old.h \ + web_api_v1.c web_api_v1.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) $(am__append_1) \ - $(am__append_2) $(am__append_3) + web_server.c web_server.h $(NULL) $(am__append_3) \ + $(am__append_4) $(am__append_5) netdata_LDADD = \ $(OPTIONAL_MATH_LIBS) \ $(OPTIONAL_NFACCT_LIBS) \ @@ -517,15 +468,25 @@ netdata_LDADD = \ $(OPTIONAL_UUID_LIBS) \ $(NULL) -apps_plugin_SOURCES = \ - apps_plugin.c \ - avl.c avl.h \ +apps_plugin_SOURCES = apps_plugin.c avl.c avl.h clocks.c clocks.h \ + common.c common.h inlined.h log.c log.h procfile.c procfile.h \ + web_buffer.c web_buffer.h $(NULL) $(am__append_6) +apps_plugin_LDADD = \ + $(OPTIONAL_MATH_LIBS) \ + $(OPTIONAL_LIBCAP_LIBS) \ + $(NULL) + +freeipmi_plugin_SOURCES = \ + freeipmi_plugin.c \ clocks.c clocks.h \ common.c common.h \ inlined.h \ log.c log.h \ procfile.c procfile.h \ - web_buffer.c web_buffer.h \ + $(NULL) + +freeipmi_plugin_LDADD = \ + $(OPTIONAL_IPMIMONITORING_LIBS) \ $(NULL) all: all-am @@ -544,6 +505,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -563,18 +525,14 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-pluginsPROGRAMS: $(plugins_PROGRAMS) @$(NORMAL_INSTALL) + test -z "$(pluginsdir)" || $(MKDIR_P) "$(DESTDIR)$(pluginsdir)" @list='$(plugins_PROGRAMS)'; test -n "$(pluginsdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pluginsdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pluginsdir)" || exit 1; \ - fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p \ - ; then echo "$$p"; echo "$$p"; else :; fi; \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ done | \ - sed -e 'p;s,.*/,,;n;h' \ - -e 's|.*|.|' \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ @@ -595,8 +553,7 @@ uninstall-pluginsPROGRAMS: @list='$(plugins_PROGRAMS)'; test -n "$(pluginsdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ - -e 's/$$/$(EXEEXT)/' \ - `; \ + -e 's/$$/$(EXEEXT)/' `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pluginsdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pluginsdir)" && rm -f $$files @@ -605,18 +562,14 @@ clean-pluginsPROGRAMS: -test -z "$(plugins_PROGRAMS)" || rm -f $(plugins_PROGRAMS) install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ - fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p \ - ; then echo "$$p"; echo "$$p"; else :; fi; \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ done | \ - sed -e 'p;s,.*/,,;n;h' \ - -e 's|.*|.|' \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ @@ -637,22 +590,22 @@ uninstall-sbinPROGRAMS: @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ - -e 's/$$/$(EXEEXT)/' \ - `; \ + -e 's/$$/$(EXEEXT)/' `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) - apps.plugin$(EXEEXT): $(apps_plugin_OBJECTS) $(apps_plugin_DEPENDENCIES) $(EXTRA_apps_plugin_DEPENDENCIES) @rm -f apps.plugin$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(apps_plugin_OBJECTS) $(apps_plugin_LDADD) $(LIBS) - + $(LINK) $(apps_plugin_OBJECTS) $(apps_plugin_LDADD) $(LIBS) +freeipmi.plugin$(EXEEXT): $(freeipmi_plugin_OBJECTS) $(freeipmi_plugin_DEPENDENCIES) $(EXTRA_freeipmi_plugin_DEPENDENCIES) + @rm -f freeipmi.plugin$(EXEEXT) + $(LINK) $(freeipmi_plugin_OBJECTS) $(freeipmi_plugin_LDADD) $(LIBS) netdata$(EXEEXT): $(netdata_OBJECTS) $(netdata_DEPENDENCIES) $(EXTRA_netdata_DEPENDENCIES) @rm -f netdata$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(netdata_OBJECTS) $(netdata_LDADD) $(LIBS) + $(LINK) $(netdata_OBJECTS) $(netdata_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -671,8 +624,12 @@ distclean-compile: @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)/freebsd_sysctl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freeipmi_plugin.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)/health_config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/health_json.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/health_log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macos_fw.Po@am__quote@ @@ -720,6 +677,17 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/registry_url.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrd2json.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrd2json_api_old.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrdcalc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrdcalctemplate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrddim.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrddimvar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrdfamily.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrdhost.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrdpush.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrdset.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrdsetvar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrdvar.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple_pattern.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage_number.Po@am__quote@ @@ -729,31 +697,30 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sys_kernel_mm_ksm.Po@am__quote@ @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_api_old.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_api_v1.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@ .c.o: -@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: -@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` install-dist_cacheDATA: $(dist_cache_DATA) @$(NORMAL_INSTALL) + test -z "$(cachedir)" || $(MKDIR_P) "$(DESTDIR)$(cachedir)" @list='$(dist_cache_DATA)'; test -n "$(cachedir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(cachedir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(cachedir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -770,11 +737,8 @@ uninstall-dist_cacheDATA: dir='$(DESTDIR)$(cachedir)'; $(am__uninstall_files_from_dir) install-dist_logDATA: $(dist_log_DATA) @$(NORMAL_INSTALL) + test -z "$(logdir)" || $(MKDIR_P) "$(DESTDIR)$(logdir)" @list='$(dist_log_DATA)'; test -n "$(logdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(logdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(logdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -791,11 +755,8 @@ uninstall-dist_logDATA: dir='$(DESTDIR)$(logdir)'; $(am__uninstall_files_from_dir) install-dist_registryDATA: $(dist_registry_DATA) @$(NORMAL_INSTALL) + test -z "$(registrydir)" || $(MKDIR_P) "$(DESTDIR)$(registrydir)" @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"; \ @@ -812,11 +773,8 @@ uninstall-dist_registryDATA: dir='$(DESTDIR)$(registrydir)'; $(am__uninstall_files_from_dir) install-dist_varlibDATA: $(dist_varlib_DATA) @$(NORMAL_INSTALL) + test -z "$(varlibdir)" || $(MKDIR_P) "$(DESTDIR)$(varlibdir)" @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"; \ @@ -832,15 +790,26 @@ uninstall-dist_varlibDATA: 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 -tags: tags-am -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ - $(am__define_uniq_tagged_files); \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ @@ -852,11 +821,15 @@ tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $$unique; \ fi; \ fi -ctags: ctags-am - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique @@ -865,21 +838,6 @@ GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-am - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags @@ -978,8 +936,7 @@ 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 + install-dvi: install-dvi-am install-dvi-am: @@ -1027,45 +984,26 @@ 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 - -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ - clean-pluginsPROGRAMS clean-sbinPROGRAMS cscopelist-am ctags \ - ctags-am distclean distclean-compile distclean-generic \ - 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-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 \ +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-pluginsPROGRAMS clean-sbinPROGRAMS ctags distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dist_cacheDATA \ + install-dist_logDATA 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 \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ uninstall-am uninstall-dist_cacheDATA uninstall-dist_logDATA \ uninstall-dist_registryDATA uninstall-dist_varlibDATA \ uninstall-pluginsPROGRAMS uninstall-sbinPROGRAMS -.PRECIOUS: Makefile - - -install-data-hook: - if [ `id -u` == 0 ]; then \ - chown root '$(DESTDIR)$(pluginsdir)/apps.plugin' && \ - chmod 0755 '$(DESTDIR)$(pluginsdir)/apps.plugin' && \ - ( setcap cap_dac_read_search,cap_sys_ptrace+ep '$(DESTDIR)$(pluginsdir)/apps.plugin' || \ - chmod 4755 '$(DESTDIR)$(pluginsdir)/apps.plugin' ); \ - else \ - echo; \ - echo "ATTENTION"; \ - echo; \ - echo "$(pluginsdir)/apps.plugin requires escalated capabilities:"; \ - echo "sudo chown root '$(DESTDIR)$(pluginsdir)/apps.plugin'"; \ - echo "sudo chmod 0755 '$(DESTDIR)$(pluginsdir)/apps.plugin'"; \ - echo "sudo setcap cap_dac_read_search,cap_sys_ptrace+ep '$(DESTDIR)$(pluginsdir)/apps.plugin'"; \ - echo; \ - fi # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/src/adaptive_resortable_list.c b/src/adaptive_resortable_list.c index a37c396fa..f74c53eae 100644 --- a/src/adaptive_resortable_list.c +++ b/src/adaptive_resortable_list.c @@ -52,7 +52,6 @@ void arl_free(ARL_BASE *arl_base) { } void arl_begin(ARL_BASE *base) { - ARL_ENTRY *e; #ifdef NETDATA_INTERNAL_CHECKS if(likely(base->iteration > 10)) { @@ -66,6 +65,7 @@ void arl_begin(ARL_BASE *base) { info("ARL '%s' has %zu fast searches and %zu slow searches. Is the source really changing so fast?" , base->name, base->fast, base->slow); + /* if(unlikely(base->iteration % 60 == 0)) { info("ARL '%s' statistics: iteration %zu, expected %zu, wanted %zu, allocated %zu, fred %zu, relinkings %zu, found %zu, added %zu, fast %zu, slow %zu" , base->name @@ -83,13 +83,15 @@ void arl_begin(ARL_BASE *base) { // for(e = base->head; e; e = e->next) fprintf(stderr, "%s ", e->name); // fprintf(stderr, "\n"); } + */ } #endif if(unlikely(base->added || base->iteration % base->rechecks) == 1) { base->added = 0; base->wanted = 0; - for(e = base->head; e ; e = e->next) { + ARL_ENTRY *e = base->head; + while(e) { if(e->flags & ARL_ENTRY_FLAG_FOUND) { // remove the found flag @@ -98,25 +100,48 @@ void arl_begin(ARL_BASE *base) { // count it in wanted if(e->flags & ARL_ENTRY_FLAG_EXPECTED) base->wanted++; + } - else if(e->flags & ARL_ENTRY_FLAG_DYNAMIC) { + else if(e->flags & ARL_ENTRY_FLAG_DYNAMIC && !(base->head == e && !e->next)) { // not last entry // we can remove this entry // it is not found, and it was created because // it was found in the source file + + // remember the next one + ARL_ENTRY *t = e->next; + + // remove it from the list if(e->next) e->next->prev = e->prev; if(e->prev) e->prev->next = e->next; if(base->head == e) base->head = e->next; + + // free it freez(e->name); freez(e); + // count it base->fred++; + + // continue + e = t; + continue; } + + e = e->next; } } + if(unlikely(!base->head)) { + // hm... no nodes at all in the list #1700 + // add a fake one to prevent a crash + // this is better than checking for the existence of nodes all the time + arl_expect(base, "a-really-not-existing-source-keyword", NULL); + } + base->iteration++; base->next_keyword = base->head; base->found = 0; + } // register an expected keyword to the ARL @@ -153,7 +178,7 @@ int arl_find_or_create_and_relink(ARL_BASE *base, const char *s, const char *val break; #ifdef NETDATA_INTERNAL_CHECKS - if(unlikely(e == base->next_keyword)) + if(unlikely(base->next_keyword && e == base->next_keyword)) fatal("Internal Error: e == base->last"); #endif @@ -208,9 +233,14 @@ int arl_find_or_create_and_relink(ARL_BASE *base, const char *s, const char *val if(base->head == base->next_keyword) base->head = e; } - else + else { e->prev = NULL; + if(!base->head) + base->head = e; + } + + // prepare the next iteration base->next_keyword = e->next; if(unlikely(!base->next_keyword)) base->next_keyword = base->head; diff --git a/src/appconfig.c b/src/appconfig.c index 81ab01be2..71ff4b75e 100644 --- a/src/appconfig.c +++ b/src/appconfig.c @@ -2,8 +2,6 @@ #define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2) -pthread_mutex_t config_mutex = PTHREAD_MUTEX_INITIALIZER; - // ---------------------------------------------------------------------------- // definitions @@ -12,7 +10,7 @@ pthread_mutex_t config_mutex = PTHREAD_MUTEX_INITIALIZER; #define CONFIG_VALUE_CHANGED 0x04 // has been changed from the loaded value #define CONFIG_VALUE_CHECKED 0x08 // has been checked if the value is different from the default -struct config_value { +struct config_option { avl avl; // the index - this has to be first! uint8_t flags; @@ -22,10 +20,10 @@ struct config_value { char *name; char *value; - struct config_value *next; // config->mutex protects just this + struct config_option *next; // config->mutex protects just this }; -struct config { +struct section { avl avl; uint32_t hash; // a simple hash to speed up searching @@ -33,111 +31,124 @@ struct config { char *name; - struct config *next; // gloabl config_mutex protects just this + struct section *next; // gloabl config_mutex protects just this - struct config_value *values; + struct config_option *values; avl_tree_lock values_index; - pthread_mutex_t mutex; // this locks only the writers, to ensure atomic updates + netdata_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; +}; + +static int appconfig_section_compare(void *a, void *b); + +struct config netdata_config = { + .sections = NULL, + .mutex = NETDATA_MUTEX_INITIALIZER, + .index = { + { NULL, appconfig_section_compare }, + AVL_LOCK_INITIALIZER + } +}; +struct config stream_config = { + .sections = NULL, + .mutex = NETDATA_MUTEX_INITIALIZER, + .index = { + { NULL, appconfig_section_compare }, + AVL_LOCK_INITIALIZER + } +}; // ---------------------------------------------------------------------------- // locking -static inline void config_global_write_lock(void) { - pthread_mutex_lock(&config_mutex); +static inline void appconfig_wrlock(struct config *root) { + netdata_mutex_lock(&root->mutex); } -static inline void config_global_unlock(void) { - pthread_mutex_unlock(&config_mutex); +static inline void appconfig_unlock(struct config *root) { + netdata_mutex_unlock(&root->mutex); } -static inline void config_section_write_lock(struct config *co) { - pthread_mutex_lock(&co->mutex); +static inline void config_section_wrlock(struct section *co) { + netdata_mutex_lock(&co->mutex); } -static inline void config_section_unlock(struct config *co) { - pthread_mutex_unlock(&co->mutex); +static inline void config_section_unlock(struct section *co) { + netdata_mutex_unlock(&co->mutex); } // ---------------------------------------------------------------------------- // 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); +static int appconfig_option_compare(void *a, void *b) { + if(((struct config_option *)a)->hash < ((struct config_option *)b)->hash) return -1; + else if(((struct config_option *)a)->hash > ((struct config_option *)b)->hash) return 1; + else return strcmp(((struct config_option *)a)->name, ((struct config_option *)b)->name); } -#define config_value_index_add(co, cv) (struct config_value *)avl_insert_lock(&((co)->values_index), (avl *)(cv)) -#define config_value_index_del(co, cv) (struct config_value *)avl_remove_lock(&((co)->values_index), (avl *)(cv)) +#define appconfig_option_index_add(co, cv) (struct config_option *)avl_insert_lock(&((co)->values_index), (avl *)(cv)) +#define appconfig_option_index_del(co, cv) (struct config_option *)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; +static struct config_option *appconfig_option_index_find(struct section *co, const char *name, uint32_t hash) { + struct config_option 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_option *)avl_search_lock(&(co->values_index), (avl *) &tmp); } // ---------------------------------------------------------------------------- // 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); +static int appconfig_section_compare(void *a, void *b) { + if(((struct section *)a)->hash < ((struct section *)b)->hash) return -1; + else if(((struct section *)a)->hash > ((struct section *)b)->hash) return 1; + else return strcmp(((struct section *)a)->name, ((struct section *)b)->name); } -avl_tree_lock config_root_index = { - { NULL, config_compare }, - AVL_LOCK_INITIALIZER -}; - -#define config_index_add(cfg) (struct config *)avl_insert_lock(&config_root_index, (avl *)(cfg)) -#define config_index_del(cfg) (struct config *)avl_remove_lock(&config_root_index, (avl *)(cfg)) +#define appconfig_index_add(root, cfg) (struct section *)avl_insert_lock(&root->index, (avl *)(cfg)) +#define appconfig_index_del(root, cfg) (struct section *)avl_remove_lock(&root->index, (avl *)(cfg)) -static struct config *config_index_find(const char *name, uint32_t hash) { - struct config tmp; +static struct section *appconfig_index_find(struct config *root, const char *name, uint32_t hash) { + struct section 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 section *)avl_search_lock(&root->index, (avl *) &tmp); } // ---------------------------------------------------------------------------- // config section methods -static inline struct config *config_section_find(const char *section) { - return config_index_find(section, 0); +static inline struct section *appconfig_section_find(struct config *root, const char *section) { + return appconfig_index_find(root, section, 0); } -static inline struct config *config_section_create(const char *section) -{ +static inline struct section *appconfig_section_create(struct config *root, const char *section) { debug(D_CONFIG, "Creating section '%s'.", section); - struct config *co = callocz(1, sizeof(struct config)); + struct section *co = callocz(1, sizeof(struct section)); 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, appconfig_option_compare); - if(unlikely(config_index_add(co) != co)) + if(unlikely(appconfig_index_add(root, co) != co)) error("INTERNAL ERROR: indexing of section '%s', already exists.", co->name); - config_global_write_lock(); - struct config *co2 = config_root; + appconfig_wrlock(root); + struct section *co2 = root->sections; if(co2) { while (co2->next) co2 = co2->next; co2->next = co; } - else config_root = co; - config_global_unlock(); + else root->sections = co; + appconfig_unlock(root); return co; } @@ -146,20 +157,25 @@ static inline struct config *config_section_create(const char *section) // ---------------------------------------------------------------------------- // config name-value methods -static inline struct config_value *config_value_create(struct config *co, const char *name, const char *value) -{ +static inline struct config_option *appconfig_value_create(struct section *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); - struct config_value *cv = callocz(1, sizeof(struct config_value)); + struct config_option *cv = callocz(1, sizeof(struct config_option)); cv->name = strdupz(name); cv->hash = simple_hash(cv->name); cv->value = strdupz(value); - if(unlikely(config_value_index_add(co, cv) != cv)) - error("INTERNAL ERROR: indexing of config '%s' in section '%s': already exists.", cv->name, co->name); + struct config_option *found = appconfig_option_index_add(co, cv); + if(found != cv) { + error("indexing of config '%s' in section '%s': already exists - using the existing one.", cv->name, co->name); + freez(cv->value); + freez(cv->name); + freez(cv); + return found; + } - config_section_write_lock(co); - struct config_value *cv2 = co->values; + config_section_wrlock(co); + struct config_option *cv2 = co->values; if(cv2) { while (cv2->next) cv2 = cv2->next; cv2->next = cv; @@ -170,66 +186,87 @@ static inline struct config_value *config_value_create(struct config *co, const return cv; } -int config_exists(const char *section, const char *name) { - struct config_value *cv; +int appconfig_exists(struct config *root, const char *section, const char *name) { + struct config_option *cv; debug(D_CONFIG, "request to get config in section '%s', name '%s'", section, name); - struct config *co = config_section_find(section); + struct section *co = appconfig_section_find(root, section); if(!co) return 0; - cv = config_value_index_find(co, name, 0); + cv = appconfig_option_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; +int appconfig_move(struct config *root, const char *section_old, const char *name_old, const char *section_new, const char *name_new) { + struct config_option *cv_old, *cv_new; + int ret = -1; - debug(D_CONFIG, "request to rename config in section '%s', old name '%s', new name '%s'", section, old, new); + debug(D_CONFIG, "request to rename config in section '%s', old name '%s', to section '%s', new name '%s'", section_old, name_old, section_new, name_new); - struct config *co = config_section_find(section); - if(!co) return -1; + struct section *co_old = appconfig_section_find(root, section_old); + if(!co_old) return ret; - config_section_write_lock(co); + struct section *co_new = appconfig_section_find(root, section_new); + if(!co_new) co_new = appconfig_section_create(root, section_new); - cv = config_value_index_find(co, old, 0); - if(!cv) goto cleanup; + config_section_wrlock(co_old); + config_section_wrlock(co_new); - cv2 = config_value_index_find(co, new, 0); - if(cv2) goto cleanup; + cv_old = appconfig_option_index_find(co_old, name_old, 0); + if(!cv_old) goto cleanup; - if(unlikely(config_value_index_del(co, cv) != cv)) - error("INTERNAL ERROR: deletion of config '%s' from section '%s', deleted tge wrong config entry.", cv->name, co->name); + cv_new = appconfig_option_index_find(co_new, name_new, 0); + if(cv_new) goto cleanup; - freez(cv->name); - cv->name = strdupz(new); - cv->hash = simple_hash(cv->name); - if(unlikely(config_value_index_add(co, cv) != cv)) - error("INTERNAL ERROR: indexing of config '%s' in section '%s', already exists.", cv->name, co->name); + if(unlikely(appconfig_option_index_del(co_old, cv_old) != cv_old)) + error("INTERNAL ERROR: deletion of config '%s' from section '%s', deleted tge wrong config entry.", cv_old->name, co_old->name); - config_section_unlock(co); + if(co_old->values == cv_old) { + co_old->values = cv_old->next; + } + else { + struct config_option *t; + for(t = co_old->values; t && t->next != cv_old ;t = t->next) ; + if(!t || t->next != cv_old) + error("INTERNAL ERROR: cannot find variable '%s' in section '%s' of the config - but it should be there.", cv_old->name, co_old->name); + else + t->next = cv_old->next; + } - return 0; + freez(cv_old->name); + cv_old->name = strdupz(name_new); + cv_old->hash = simple_hash(cv_old->name); + + cv_new = cv_old; + cv_new->next = co_new->values; + co_new->values = cv_new; + + if(unlikely(appconfig_option_index_add(co_new, cv_old) != cv_old)) + error("INTERNAL ERROR: re-indexing of config '%s' in section '%s', already exists.", cv_old->name, co_new->name); + + ret = 0; cleanup: - config_section_unlock(co); - return -1; + config_section_unlock(co_new); + config_section_unlock(co_old); + return ret; } -char *config_get(const char *section, const char *name, const char *default_value) +char *appconfig_get(struct config *root, const char *section, const char *name, const char *default_value) { - struct config_value *cv; + struct config_option *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); + struct section *co = appconfig_section_find(root, section); + if(!co) co = appconfig_section_create(root, section); - cv = config_value_index_find(co, name, 0); + cv = appconfig_option_index_find(co, name, 0); if(!cv) { - cv = config_value_create(co, name, default_value); + cv = appconfig_value_create(co, name, default_value); if(!cv) return NULL; } cv->flags |= CONFIG_VALUE_USED; @@ -246,67 +283,67 @@ char *config_get(const char *section, const char *name, const char *default_valu return(cv->value); } -long long config_get_number(const char *section, const char *name, long long value) +long long appconfig_get_number(struct config *root, const char *section, const char *name, long long value) { char buffer[100], *s; sprintf(buffer, "%lld", value); - s = config_get(section, name, buffer); + s = appconfig_get(root, section, name, buffer); if(!s) return value; return strtoll(s, NULL, 0); } -int config_get_boolean(const char *section, const char *name, int value) +int appconfig_get_boolean(struct config *root, const char *section, const char *name, int value) { char *s; if(value) s = "yes"; else s = "no"; - s = config_get(section, name, s); + s = appconfig_get(root, section, name, s); if(!s) return value; 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) +int appconfig_get_boolean_ondemand(struct config *root, const char *section, const char *name, int value) { char *s; - if(value == CONFIG_ONDEMAND_ONDEMAND) + if(value == CONFIG_BOOLEAN_AUTO) s = "auto"; - else if(value == CONFIG_ONDEMAND_NO) + else if(value == CONFIG_BOOLEAN_NO) s = "no"; else s = "yes"; - s = config_get(section, name, s); + s = appconfig_get(root, section, name, s); if(!s) return value; if(!strcmp(s, "yes")) - return CONFIG_ONDEMAND_YES; + return CONFIG_BOOLEAN_YES; else if(!strcmp(s, "no")) - return CONFIG_ONDEMAND_NO; + return CONFIG_BOOLEAN_NO; else if(!strcmp(s, "auto") || !strcmp(s, "on demand")) - return CONFIG_ONDEMAND_ONDEMAND; + return CONFIG_BOOLEAN_AUTO; return value; } -const char *config_set_default(const char *section, const char *name, const char *value) +const char *appconfig_set_default(struct config *root, const char *section, const char *name, const char *value) { - struct config_value *cv; + struct config_option *cv; 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 section *co = appconfig_section_find(root, section); + if(!co) return appconfig_set(root, section, name, value); - cv = config_value_index_find(co, name, 0); - if(!cv) return config_set(section, name, value); + cv = appconfig_option_index_find(co, name, 0); + if(!cv) return appconfig_set(root, section, name, value); cv->flags |= CONFIG_VALUE_USED; @@ -323,17 +360,17 @@ const char *config_set_default(const char *section, const char *name, const char return cv->value; } -const char *config_set(const char *section, const char *name, const char *value) +const char *appconfig_set(struct config *root, const char *section, const char *name, const char *value) { - struct config_value *cv; + struct config_option *cv; 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 section *co = appconfig_section_find(root, section); + if(!co) co = appconfig_section_create(root, section); - cv = config_value_index_find(co, name, 0); - if(!cv) cv = config_value_create(co, name, value); + cv = appconfig_option_index_find(co, name, 0); + if(!cv) cv = appconfig_value_create(co, name, value); cv->flags |= CONFIG_VALUE_USED; if(strcmp(cv->value, value) != 0) { @@ -346,23 +383,23 @@ const char *config_set(const char *section, const char *name, const char *value) return value; } -long long config_set_number(const char *section, const char *name, long long value) +long long appconfig_set_number(struct config *root, const char *section, const char *name, long long value) { char buffer[100]; sprintf(buffer, "%lld", value); - config_set(section, name, buffer); + appconfig_set(root, section, name, buffer); return value; } -int config_set_boolean(const char *section, const char *name, int value) +int appconfig_set_boolean(struct config *root, const char *section, const char *name, int value) { char *s; if(value) s = "yes"; else s = "no"; - config_set(section, name, s); + appconfig_set(root, section, name, s); return value; } @@ -371,10 +408,10 @@ int config_set_boolean(const char *section, const char *name, int value) // ---------------------------------------------------------------------------- // config load/save -int load_config(char *filename, int overwrite_used) +int appconfig_load(struct config *root, char *filename, int overwrite_used) { int line = 0; - struct config *co = NULL; + struct section *co = NULL; char buffer[CONFIG_FILE_LINE_MAX + 1], *s; @@ -404,8 +441,8 @@ int load_config(char *filename, int overwrite_used) s[len - 1] = '\0'; s++; - co = config_section_find(s); - if(!co) co = config_section_create(s); + co = appconfig_section_find(root, s); + if(!co) co = appconfig_section_create(root, s); continue; } @@ -437,9 +474,9 @@ int load_config(char *filename, int overwrite_used) continue; } - struct config_value *cv = config_value_index_find(co, name, 0); + struct config_option *cv = appconfig_option_index_find(co, name, 0); - if(!cv) cv = config_value_create(co, name, value); + if(!cv) cv = appconfig_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); @@ -457,11 +494,11 @@ int load_config(char *filename, int overwrite_used) return 1; } -void generate_config(BUFFER *wb, int only_changed) +void appconfig_generate(struct config *root, BUFFER *wb, int only_changed) { int i, pri; - struct config *co; - struct config_value *cv; + struct section *co; + struct config_option *cv; for(i = 0; i < 3 ;i++) { switch(i) { @@ -490,13 +527,16 @@ void generate_config(BUFFER *wb, int only_changed) 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") || - !strcmp(co->name, "backend")) + appconfig_wrlock(root); + for(co = root->sections; co ; co = co->next) { + if(!strcmp(co->name, CONFIG_SECTION_GLOBAL) + || !strcmp(co->name, CONFIG_SECTION_WEB) + || !strcmp(co->name, CONFIG_SECTION_PLUGINS) + || !strcmp(co->name, CONFIG_SECTION_REGISTRY) + || !strcmp(co->name, CONFIG_SECTION_HEALTH) + || !strcmp(co->name, CONFIG_SECTION_BACKEND) + || !strcmp(co->name, CONFIG_SECTION_STREAM) + ) pri = 0; else if(!strncmp(co->name, "plugin:", 7)) pri = 1; else pri = 2; @@ -506,7 +546,7 @@ void generate_config(BUFFER *wb, int only_changed) int changed = 0; int count = 0; - config_section_write_lock(co); + config_section_wrlock(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; @@ -523,7 +563,7 @@ void generate_config(BUFFER *wb, int only_changed) buffer_sprintf(wb, "\n[%s]\n", co->name); - config_section_write_lock(co); + config_section_wrlock(co); for(cv = co->values; cv ; cv = cv->next) { if(used && !(cv->flags & CONFIG_VALUE_USED)) { @@ -534,6 +574,6 @@ void generate_config(BUFFER *wb, int only_changed) config_section_unlock(co); } } - config_global_unlock(); + appconfig_unlock(root); } } diff --git a/src/appconfig.h b/src/appconfig.h index 08aae8348..45cc8cfd5 100644 --- a/src/appconfig.h +++ b/src/appconfig.h @@ -3,30 +3,67 @@ #define CONFIG_FILENAME "netdata.conf" +#define CONFIG_SECTION_GLOBAL "global" +#define CONFIG_SECTION_WEB "web" +#define CONFIG_SECTION_PLUGINS "plugins" +#define CONFIG_SECTION_REGISTRY "registry" +#define CONFIG_SECTION_HEALTH "health" +#define CONFIG_SECTION_BACKEND "backend" +#define CONFIG_SECTION_STREAM "stream" + // these are used to limit the configuration names and values lengths // they are not enforced by config.c functions (they will strdup() all strings, no matter of their length) #define CONFIG_MAX_NAME 1024 #define CONFIG_MAX_VALUE 2048 -extern int load_config(char *filename, int overwrite_used); +struct config { + struct section *sections; + netdata_mutex_t mutex; + avl_tree_lock index; +}; + +extern struct config + netdata_config, + stream_config; + +#define CONFIG_BOOLEAN_NO 0 +#define CONFIG_BOOLEAN_YES 1 +#define CONFIG_BOOLEAN_AUTO 2 + +extern int appconfig_load(struct config *root, char *filename, int overwrite_used); + +extern char *appconfig_get(struct config *root, const char *section, const char *name, const char *default_value); +extern long long appconfig_get_number(struct config *root, const char *section, const char *name, long long value); +extern int appconfig_get_boolean(struct config *root, const char *section, const char *name, int value); +extern int appconfig_get_boolean_ondemand(struct config *root, const char *section, const char *name, int value); + +extern const char *appconfig_set(struct config *root, const char *section, const char *name, const char *value); +extern const char *appconfig_set_default(struct config *root, const char *section, const char *name, const char *value); +extern long long appconfig_set_number(struct config *root, const char *section, const char *name, long long value); +extern int appconfig_set_boolean(struct config *root, const char *section, const char *name, int value); + +extern int appconfig_exists(struct config *root, const char *section, const char *name); +extern int appconfig_move(struct config *root, const char *section_old, const char *name_old, const char *section_new, const char *name_new); + +extern void appconfig_generate(struct config *root, BUFFER *wb, int only_changed); -extern char *config_get(const char *section, const char *name, const char *default_value); -extern long long config_get_number(const char *section, const char *name, long long value); -extern int config_get_boolean(const char *section, const char *name, int value); +// ---------------------------------------------------------------------------- +// shortcuts for the default netdata configuration -#define CONFIG_ONDEMAND_NO 0 -#define CONFIG_ONDEMAND_YES 1 -#define CONFIG_ONDEMAND_ONDEMAND 2 -extern int config_get_boolean_ondemand(const char *section, const char *name, int value); +#define config_load(filename, overwrite_used) appconfig_load(&netdata_config, filename, overwrite_used) +#define config_get(section, name, default_value) appconfig_get(&netdata_config, section, name, default_value) +#define config_get_number(section, name, value) appconfig_get_number(&netdata_config, section, name, value) +#define config_get_boolean(section, name, value) appconfig_get_boolean(&netdata_config, section, name, value) +#define config_get_boolean_ondemand(section, name, value) appconfig_get_boolean_ondemand(&netdata_config, section, name, value) -extern const char *config_set(const char *section, const char *name, const char *value); -extern const char *config_set_default(const char *section, const char *name, const char *value); -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); +#define config_set(section, name, default_value) appconfig_get(&netdata_config, section, name, default_value) +#define config_set_default(section, name, value) appconfig_set_default(&netdata_config, section, name, value) +#define config_set_number(section, name, value) appconfig_set_number(&netdata_config, section, name, value) +#define config_set_boolean(section, name, value) appconfig_set_boolean(&netdata_config, section, name, value) -extern int config_exists(const char *section, const char *name); -extern int config_rename(const char *section, const char *old, const char *new); +#define config_exists(section, name) appconfig_exists(&netdata_config, section, name) +#define config_move(section_old, name_old, section_new, name_new) appconfig_move(&netdata_config, section_old, name_old, section_new, name_new) -extern void generate_config(BUFFER *wb, int only_changed); +#define config_generate(buffer, only_changed) appconfig_generate(&netdata_config, buffer, only_changed) #endif /* NETDATA_CONFIG_H */ diff --git a/src/apps_plugin.c b/src/apps_plugin.c index 0a72190aa..b1bf06bee 100644 --- a/src/apps_plugin.c +++ b/src/apps_plugin.c @@ -1,50 +1,141 @@ + +/* + * netdata apps.plugin + * (C) Copyright 2016-2017 Costa Tsaousis <costa@tsaousis.gr> + * Released under GPL v3+ + */ + #include "common.h" +#ifdef __FreeBSD__ +#include <sys/user.h> +#endif + +// ---------------------------------------------------------------------------- +// per O/S configuration + +// the minimum PID of the system +// this is also the pid of the init process +#define INIT_PID 1 + +// if the way apps.plugin will work, will read the entire process list, +// including the resource utilization of each process, instantly +// set this to 1 +// when set to 0, apps.plugin builds a sort list of processes, in order +// to process children processes, before parent processes +#ifdef __FreeBSD__ +#define ALL_PIDS_ARE_READ_INSTANTLY 1 +#else +#define ALL_PIDS_ARE_READ_INSTANTLY 0 +#endif + +// ---------------------------------------------------------------------------- +// string lengths + #define MAX_COMPARE_NAME 100 #define MAX_NAME 100 #define MAX_CMDLINE 1024 -// 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 + +// ---------------------------------------------------------------------------- +// 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 + +// ---------------------------------------------------------------------------- +// to avoid reallocating too frequently, we can increase the number of spare +// file descriptors used by processes. +// IMPORTANT: +// having a lot of spares, increases the CPU utilization of the plugin. #define MAX_SPARE_FDS 1 -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 *config_dir = CONFIG_DIR; +// ---------------------------------------------------------------------------- +// command line options + +static int + debug = 0, + update_every = 1, + enable_guest_charts = 0, +#ifdef __FreeBSD__ + enable_file_charts = 0, +#else + enable_file_charts = 1, +#endif + enable_users_charts = 1, + enable_groups_charts = 1, + include_exited_childs = 1; -pid_t *all_pids_sortlist = NULL; -// will be automatically set to 1, if guest values are collected -int show_guest_time = 0; -int show_guest_time_old = 0; +// will be changed to getenv(NETDATA_CONFIG_DIR) if it exists +static char *config_dir = CONFIG_DIR; + +// ---------------------------------------------------------------------------- +// internal flags +// handled in code (automatically set) + +static int + show_guest_time = 0, // 1 when guest values are collected + show_guest_time_old = 0, + proc_pid_cmdline_is_needed = 0; // 1 when we need to read /proc/cmdline -int enable_guest_charts = 0; -int enable_file_charts = 1; -int enable_users_charts = 1; -int enable_groups_charts = 1; // ---------------------------------------------------------------------------- +// internal counters + +static size_t + global_iterations_counter = 1, + calls_counter = 0, + file_counter = 0, + targets_assignment_counter = 0; + + +// ---------------------------------------------------------------------------- +// Normalization +// +// With normalization we lower the collected metrics by a factor to make them +// match the total utilization of the system. +// The discrepancy exists because apps.plugin needs some time to collect all +// the metrics. This results in utilization that exceeds the total utilization +// of the system. +// +// With normalization we align the per-process utilization, to the total of +// the system. We first consume the exited children utilization and it the +// collected values is above the total, we proportionally scale each reported +// metric. + +// the total system time, as reported by /proc/stat +static kernel_uint_t + global_utime = 0, + global_stime = 0, + global_gtime = 0; -void netdata_cleanup_and_exit(int ret) { - exit(ret); -} + +// the normalization ratios, as calculated by normalize_utilization() +double utime_fix_ratio = 1.0, + stime_fix_ratio = 1.0, + gtime_fix_ratio = 1.0, + minflt_fix_ratio = 1.0, + majflt_fix_ratio = 1.0, + cutime_fix_ratio = 1.0, + cstime_fix_ratio = 1.0, + cgtime_fix_ratio = 1.0, + cminflt_fix_ratio = 1.0, + cmajflt_fix_ratio = 1.0; // ---------------------------------------------------------------------------- // target -// target is the structure that process data are aggregated +// +// target is the structure that processes are aggregated to be reported +// to netdata. +// +// - Each entry in /etc/apps_groups.conf creates a target. +// - Each user and group used by a process in the system, creates a target. struct target { char compare[MAX_COMPARE_NAME + 1]; @@ -59,74 +150,281 @@ struct target { 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; + kernel_uint_t minflt; + kernel_uint_t cminflt; + kernel_uint_t majflt; + kernel_uint_t cmajflt; + kernel_uint_t utime; + kernel_uint_t stime; + kernel_uint_t gtime; + kernel_uint_t cutime; + kernel_uint_t cstime; + kernel_uint_t cgtime; + kernel_uint_t num_threads; + // kernel_uint_t rss; + + kernel_uint_t statm_size; + kernel_uint_t statm_resident; + kernel_uint_t statm_share; + // kernel_uint_t statm_text; + // kernel_uint_t statm_lib; + // kernel_uint_t statm_data; + // kernel_uint_t statm_dirty; + + kernel_uint_t io_logical_bytes_read; + kernel_uint_t io_logical_bytes_written; + // kernel_uint_t io_read_calls; + // kernel_uint_t io_write_calls; + kernel_uint_t io_storage_bytes_read; + kernel_uint_t io_storage_bytes_written; + // kernel_uint_t io_cancelled_write_bytes; int *target_fds; int target_fds_size; - 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 + kernel_uint_t openfiles; + kernel_uint_t openpipes; + kernel_uint_t opensockets; + kernel_uint_t openinotifies; + kernel_uint_t openeventfds; + kernel_uint_t opentimerfds; + kernel_uint_t opensignalfds; + kernel_uint_t openeventpolls; + kernel_uint_t openother; + + unsigned int 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 + 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 *target; // the one that will be reported to netdata struct target *next; }; +struct target + *apps_groups_default_target = NULL, // the default target + *apps_groups_root_target = NULL, // apps_groups.conf defined + *users_root_target = NULL, // users + *groups_root_target = NULL; // user groups + +size_t + apps_groups_targets_count = 0; // # of apps_groups.conf targets + // ---------------------------------------------------------------------------- -// apps_groups.conf -// aggregate all processes in groups, to have a limited number of dimensions +// pid_stat +// +// structure to store data for each process running +// see: man proc for the description of the fields + +struct pid_stat { + int32_t pid; + char comm[MAX_COMPARE_NAME + 1]; + char cmdline[MAX_CMDLINE + 1]; -struct target *apps_groups_root_target = NULL; -struct target *apps_groups_default_target = NULL; -long apps_groups_targets = 0; + uint32_t log_thrown; -struct target *users_root_target = NULL; -struct target *groups_root_target = NULL; + // char state; + int32_t ppid; + // int32_t pgrp; + // int32_t session; + // int32_t tty_nr; + // int32_t tpgid; + // uint64_t flags; -static struct target *get_users_target(uid_t uid) -{ + // these are raw values collected + kernel_uint_t minflt_raw; + kernel_uint_t cminflt_raw; + kernel_uint_t majflt_raw; + kernel_uint_t cmajflt_raw; + kernel_uint_t utime_raw; + kernel_uint_t stime_raw; + kernel_uint_t gtime_raw; // guest_time + kernel_uint_t cutime_raw; + kernel_uint_t cstime_raw; + kernel_uint_t cgtime_raw; // cguest_time + + // these are rates + kernel_uint_t minflt; + kernel_uint_t cminflt; + kernel_uint_t majflt; + kernel_uint_t cmajflt; + kernel_uint_t utime; + kernel_uint_t stime; + kernel_uint_t gtime; + kernel_uint_t cutime; + kernel_uint_t cstime; + kernel_uint_t cgtime; + + // int64_t priority; + // int64_t nice; + int32_t num_threads; + // int64_t itrealvalue; + // kernel_uint_t starttime; + // kernel_uint_t vsize; + // kernel_uint_t rss; + // kernel_uint_t rsslim; + // kernel_uint_t starcode; + // kernel_uint_t endcode; + // kernel_uint_t startstack; + // kernel_uint_t kstkesp; + // kernel_uint_t 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; + // kernel_uint_t delayacct_blkio_ticks; + + uid_t uid; + gid_t gid; + + kernel_uint_t statm_size; + kernel_uint_t statm_resident; + kernel_uint_t statm_share; + // kernel_uint_t statm_text; + // kernel_uint_t statm_lib; + // kernel_uint_t statm_data; + // kernel_uint_t statm_dirty; + + kernel_uint_t io_logical_bytes_read_raw; + kernel_uint_t io_logical_bytes_written_raw; + // kernel_uint_t io_read_calls_raw; + // kernel_uint_t io_write_calls_raw; + kernel_uint_t io_storage_bytes_read_raw; + kernel_uint_t io_storage_bytes_written_raw; + // kernel_uint_t io_cancelled_write_bytes_raw; + + kernel_uint_t io_logical_bytes_read; + kernel_uint_t io_logical_bytes_written; + // kernel_uint_t io_read_calls; + // kernel_uint_t io_write_calls; + kernel_uint_t io_storage_bytes_read; + kernel_uint_t io_storage_bytes_written; + // kernel_uint_t 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 + char keep:1; // 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 + char updated:1; // 1 when the process is currently running + char merged:1; // 1 when it has been merged to its parent + char read:1; // 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 + + usec_t stat_collected_usec; + usec_t last_stat_collected_usec; + + usec_t io_collected_usec; + usec_t last_io_collected_usec; + + char *fds_dirname; // the full directory name in /proc/PID/fd + + char *stat_filename; + char *statm_filename; + char *io_filename; + char *cmdline_filename; + + struct pid_stat *parent; + struct pid_stat *prev; + struct pid_stat *next; +}; + +// log each problem once per process +// log flood protection flags (log_thrown) +#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 + +static struct pid_stat + *root_of_pids = NULL, // global list of all processes running + **all_pids = NULL; // to avoid allocations, we pre-allocate the + // the entire pid space. + +static size_t + all_pids_count = 0; // the number of processes running + +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) +// Another pre-allocated list of all possible pids. +// We need it to pids and assign them a unique sortlist id, so that we +// read parents before children. 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. +static pid_t + *all_pids_sortlist = NULL; +#endif + +// ---------------------------------------------------------------------------- +// file descriptor +// +// this is used to keep a global list of all open files of the system. +// it is needed in order to calculate the unique files processes have open. + +#define FILE_DESCRIPTORS_INCREASE_STEP 100 + +// types for struct file_descriptor->type +typedef enum fd_filetype { + FILETYPE_OTHER, + FILETYPE_FILE, + FILETYPE_PIPE, + FILETYPE_SOCKET, + FILETYPE_INOTIFY, + FILETYPE_EVENTFD, + FILETYPE_EVENTPOLL, + FILETYPE_TIMERFD, + FILETYPE_SIGNALFD +} FD_FILETYPE; + +struct file_descriptor { + avl avl; + +#ifdef NETDATA_INTERNAL_CHECKS + uint32_t magic; +#endif /* NETDATA_INTERNAL_CHECKS */ + + const char *name; + uint32_t hash; + + FD_FILETYPE type; + int count; + int pos; +} *all_files = NULL; + +static int + all_files_len = 0, + all_files_size = 0; + +// ---------------------------------------------------------------------------- +// callback required by fatal() + +void netdata_cleanup_and_exit(int ret) { + exit(ret); +} + +// ---------------------------------------------------------------------------- +// apps_groups.conf +// aggregate all processes in groups, to have a limited number of dimensions + +static 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; @@ -221,10 +519,12 @@ static struct target *get_apps_groups_target(const char *id, struct target *targ if(*name == '-') thidden = 1; name++; } - for(target = apps_groups_root_target ; target ; target = target->next) { + + for(target = apps_groups_root_target ; target != NULL ; target = target->next) { if(!target->target && strcmp(name, target->name) == 0) break; } + if(unlikely(debug)) { if(unlikely(target)) fprintf(stderr, "apps.plugin: REUSING TARGET NAME '%s' on ID '%s'\n", target->name, target->id); @@ -302,10 +602,10 @@ static int read_apps_groups_conf(const char *file) if(!ff) return 1; - unsigned long line, lines = procfile_lines(ff); + size_t line, lines = procfile_lines(ff); for(line = 0; line < lines ;line++) { - unsigned long word, words = procfile_linewords(ff, line); + size_t word, words = procfile_linewords(ff, line); if(!words) continue; char *name = procfile_lineword(ff, line, 0); @@ -326,7 +626,7 @@ static int read_apps_groups_conf(const char *file) // add this target struct target *n = get_apps_groups_target(s, w, name); if(!n) { - error("Cannot create target '%s' (line %lu, word %lu)", s, line, word); + error("Cannot create target '%s' (line %zu, word %zu)", s, line, word); continue; } @@ -351,186 +651,135 @@ static int read_apps_groups_conf(const char *file) // ---------------------------------------------------------------------------- -// data to store for each pid -// see: man proc +// struct pid_stat management -#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 +static inline struct pid_stat *get_pid_entry(pid_t pid) { + if(unlikely(all_pids[pid])) + return all_pids[pid]; -struct pid_stat { - int32_t pid; - char comm[MAX_COMPARE_NAME + 1]; - char cmdline[MAX_CMDLINE + 1]; + struct pid_stat *p = callocz(sizeof(struct pid_stat), 1); + p->fds = callocz(sizeof(int), MAX_SPARE_FDS); + p->fds_size = MAX_SPARE_FDS; - uint32_t log_thrown; + if(likely(root_of_pids)) + root_of_pids->prev = p; - // char state; - int32_t ppid; - // int32_t pgrp; - // int32_t session; - // int32_t tty_nr; - // int32_t tpgid; - // uint64_t flags; + p->next = root_of_pids; + root_of_pids = p; - // 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 + p->pid = pid; - // 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; + all_pids[pid] = p; + all_pids_count++; - // 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; + return p; +} - uid_t uid; - gid_t gid; +static inline void del_pid_entry(pid_t pid) { + struct pid_stat *p = all_pids[pid]; - 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; + if(unlikely(!p)) { + error("attempted to free pid %d that is not allocated.", pid); + return; + } - int *fds; // array of fds it uses - int fds_size; // the size of the fds array + if(unlikely(debug)) + fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, p->comm); - 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 + if(root_of_pids == p) + root_of_pids = p->next; - struct target *target; // app_groups.conf targets - struct target *user_target; // uid based targets - struct target *group_target; // gid based targets + if(p->next) p->next->prev = p->prev; + if(p->prev) p->prev->next = p->next; - unsigned long long stat_collected_usec; - unsigned long long last_stat_collected_usec; + freez(p->fds); + freez(p->fds_dirname); + freez(p->stat_filename); + freez(p->statm_filename); + freez(p->io_filename); + freez(p->cmdline_filename); + freez(p); - unsigned long long io_collected_usec; - unsigned long long last_io_collected_usec; + all_pids[pid] = NULL; + all_pids_count--; +} - 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; +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); -long all_pids_count = 0; + 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')", netdata_configured_host_prefix, p->pid, p->comm); + break; -static inline struct pid_stat *get_pid_entry(pid_t pid) { - if(all_pids[pid]) { - all_pids[pid]->new_entry = 0; - return all_pids[pid]; - } + case PID_LOG_STATM: + error("Cannot process %s/proc/%d/statm (command '%s')", netdata_configured_host_prefix, p->pid, p->comm); + break; - all_pids[pid] = callocz(sizeof(struct pid_stat), 1); - all_pids[pid]->fds = callocz(sizeof(int), MAX_SPARE_FDS); - all_pids[pid]->fds_size = MAX_SPARE_FDS; + case PID_LOG_CMDLINE: + error("Cannot process %s/proc/%d/cmdline (command '%s')", netdata_configured_host_prefix, p->pid, p->comm); + break; - if(root_of_pids) root_of_pids->prev = all_pids[pid]; - all_pids[pid]->next = root_of_pids; - root_of_pids = all_pids[pid]; + case PID_LOG_FDS: + error("Cannot process entries in %s/proc/%d/fd (command '%s')", netdata_configured_host_prefix, p->pid, p->comm); + break; - all_pids[pid]->pid = pid; - all_pids[pid]->new_entry = 1; + case PID_LOG_STAT: + break; - all_pids_count++; + 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 all_pids[pid]; + return status; } -static inline void del_pid_entry(pid_t pid) { - if(!all_pids[pid]) { - error("attempted to free pid %d that is not allocated.", pid); - return; - } +static inline void assign_target_to_pid(struct pid_stat *p) { + targets_assignment_counter++; - if(unlikely(debug)) - fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm); + uint32_t hash = simple_hash(p->comm); + size_t pclen = strlen(p->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; + 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); - 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]); + // 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 - all_pids[pid] = NULL; - all_pids_count--; + if(unlikely(( (!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; + } + } } @@ -539,9 +788,20 @@ static inline void del_pid_entry(pid_t pid) { static inline int read_proc_pid_cmdline(struct pid_stat *p) { +#ifdef __FreeBSD__ + size_t i, bytes = MAX_CMDLINE; + int mib[4]; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ARGS; + mib[3] = p->pid; + if (unlikely(sysctl(mib, 4, p->cmdline, &bytes, NULL, 0))) + goto cleanup; +#else if(unlikely(!p->cmdline_filename)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", global_host_prefix, p->pid); + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", netdata_configured_host_prefix, p->pid); p->cmdline_filename = strdupz(filename); } @@ -552,6 +812,7 @@ static inline int read_proc_pid_cmdline(struct pid_stat *p) { close(fd); if(unlikely(bytes < 0)) goto cleanup; +#endif p->cmdline[bytes] = '\0'; for(i = 0; i < bytes ; i++) @@ -568,7 +829,16 @@ cleanup: return 0; } -static inline int read_proc_pid_ownership(struct pid_stat *p) { +static inline int read_proc_pid_ownership(struct pid_stat *p, void *ptr) { + (void)ptr; +#ifdef __FreeBSD__ + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr; + + p->uid = proc_info->ki_uid; + p->gid = proc_info->ki_groups[0]; + + return 1; +#else if(unlikely(!p->stat_filename)) { error("pid %d does not have a stat_filename", p->pid); return 0; @@ -587,14 +857,41 @@ static inline int read_proc_pid_ownership(struct pid_stat *p) { p->gid = st.st_gid; return 1; +#endif } -static inline int read_proc_pid_stat(struct pid_stat *p) { +// ---------------------------------------------------------------------------- +// macro to calculate the incremental rate of a value +// each parameter is accessed only ONCE - so it is safe to pass function calls +// or other macros as parameters + +#define incremental_rate(rate_variable, last_kernel_variable, new_kernel_value, collected_usec, last_collected_usec) { \ + kernel_uint_t _new_tmp = new_kernel_value; \ + rate_variable = (_new_tmp - last_kernel_variable) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); \ + last_kernel_variable = _new_tmp; \ + } + +// the same macro for struct pid members +#define pid_incremental_rate(type, var, value) \ + incremental_rate(var, var##_raw, value, p->type##_collected_usec, p->last_##type##_collected_usec) + + +// ---------------------------------------------------------------------------- + +static inline int read_proc_pid_stat(struct pid_stat *p, void *ptr) { + (void)ptr; + +#ifdef __FreeBSD__ + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr; + + if (unlikely(proc_info->ki_tdflags & TDF_IDLETD)) + goto cleanup; +#else static procfile *ff = NULL; if(unlikely(!p->stat_filename)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", global_host_prefix, p->pid); + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", netdata_configured_host_prefix, p->pid); p->stat_filename = strdupz(filename); } @@ -604,95 +901,104 @@ static inline int read_proc_pid_stat(struct pid_stat *p) { if(unlikely(!ff)) goto cleanup; // if(set_quotes) procfile_set_quotes(ff, "()"); - if(set_quotes) procfile_set_open_close(ff, "(", ")"); + if(unlikely(set_quotes)) + procfile_set_open_close(ff, "(", ")"); ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif p->last_stat_collected_usec = p->stat_collected_usec; - p->stat_collected_usec = now_realtime_usec(); - file_counter++; + p->stat_collected_usec = now_monotonic_usec(); + calls_counter++; + +#ifdef __FreeBSD__ + char *comm = proc_info->ki_comm; + p->ppid = proc_info->ki_ppid; +#else + // p->pid = str2pid_t(procfile_lineword(ff, 0, 0)); + char *comm = procfile_lineword(ff, 0, 1); + // p->state = *(procfile_lineword(ff, 0, 2)); + p->ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3)); + // p->pgrp = (int32_t)str2pid_t(procfile_lineword(ff, 0, 4)); + // p->session = (int32_t)str2pid_t(procfile_lineword(ff, 0, 5)); + // p->tty_nr = (int32_t)str2pid_t(procfile_lineword(ff, 0, 6)); + // p->tpgid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 7)); + // p->flags = str2uint64_t(procfile_lineword(ff, 0, 8)); +#endif - // p->pid = str2ul(procfile_lineword(ff, 0, 0+i)); + if(strcmp(p->comm, comm)) { + if(unlikely(debug)) { + if(p->comm[0]) + fprintf(stderr, "apps.plugin: \tpid %d (%s) changed name to '%s'\n", p->pid, p->comm, comm); + else + fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", p->pid, comm); + } - strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME); + strncpyz(p->comm, comm, MAX_COMPARE_NAME); - // p->state = *(procfile_lineword(ff, 0, 2)); - p->ppid = (int32_t)str2ul(procfile_lineword(ff, 0, 3)); - // p->pgrp = str2ul(procfile_lineword(ff, 0, 4)); - // p->session = str2ul(procfile_lineword(ff, 0, 5)); - // p->tty_nr = str2ul(procfile_lineword(ff, 0, 6)); - // p->tpgid = str2ul(procfile_lineword(ff, 0, 7)); - // p->flags = str2ull(procfile_lineword(ff, 0, 8)); - - unsigned long long last; - - last = p->minflt_raw; - p->minflt_raw = str2ull(procfile_lineword(ff, 0, 9)); - p->minflt = (p->minflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - last = p->cminflt_raw; - p->cminflt_raw = str2ull(procfile_lineword(ff, 0, 10)); - p->cminflt = (p->cminflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - last = p->majflt_raw; - p->majflt_raw = str2ull(procfile_lineword(ff, 0, 11)); - p->majflt = (p->majflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - last = p->cmajflt_raw; - p->cmajflt_raw = str2ull(procfile_lineword(ff, 0, 12)); - p->cmajflt = (p->cmajflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - last = p->utime_raw; - p->utime_raw = str2ull(procfile_lineword(ff, 0, 13)); - p->utime = (p->utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - last = p->stime_raw; - p->stime_raw = str2ull(procfile_lineword(ff, 0, 14)); - p->stime = (p->stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - last = p->cutime_raw; - p->cutime_raw = str2ull(procfile_lineword(ff, 0, 15)); - p->cutime = (p->cutime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - last = p->cstime_raw; - p->cstime_raw = str2ull(procfile_lineword(ff, 0, 16)); - p->cstime = (p->cstime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - // p->priority = str2ull(procfile_lineword(ff, 0, 17)); - // p->nice = str2ull(procfile_lineword(ff, 0, 18)); - p->num_threads = (int32_t)str2ul(procfile_lineword(ff, 0, 19)); - // p->itrealvalue = str2ull(procfile_lineword(ff, 0, 20)); - // p->starttime = str2ull(procfile_lineword(ff, 0, 21)); - // p->vsize = str2ull(procfile_lineword(ff, 0, 22)); - // p->rss = str2ull(procfile_lineword(ff, 0, 23)); - // p->rsslim = str2ull(procfile_lineword(ff, 0, 24)); - // p->starcode = str2ull(procfile_lineword(ff, 0, 25)); - // p->endcode = str2ull(procfile_lineword(ff, 0, 26)); - // p->startstack = str2ull(procfile_lineword(ff, 0, 27)); - // p->kstkesp = str2ull(procfile_lineword(ff, 0, 28)); - // p->kstkeip = str2ull(procfile_lineword(ff, 0, 29)); - // p->signal = str2ull(procfile_lineword(ff, 0, 30)); - // p->blocked = str2ull(procfile_lineword(ff, 0, 31)); - // p->sigignore = str2ull(procfile_lineword(ff, 0, 32)); - // p->sigcatch = str2ull(procfile_lineword(ff, 0, 33)); - // p->wchan = str2ull(procfile_lineword(ff, 0, 34)); - // p->nswap = str2ull(procfile_lineword(ff, 0, 35)); - // p->cnswap = str2ull(procfile_lineword(ff, 0, 36)); - // p->exit_signal = str2ul(procfile_lineword(ff, 0, 37)); - // p->processor = str2ul(procfile_lineword(ff, 0, 38)); - // p->rt_priority = str2ul(procfile_lineword(ff, 0, 39)); - // p->policy = str2ul(procfile_lineword(ff, 0, 40)); - // p->delayacct_blkio_ticks = str2ull(procfile_lineword(ff, 0, 41)); + // /proc/<pid>/cmdline + if(likely(proc_pid_cmdline_is_needed)) + managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p)); + + assign_target_to_pid(p); + } + +#ifdef __FreeBSD__ + pid_incremental_rate(stat, p->minflt, (kernel_uint_t)proc_info->ki_rusage.ru_minflt); + pid_incremental_rate(stat, p->cminflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_minflt); + pid_incremental_rate(stat, p->majflt, (kernel_uint_t)proc_info->ki_rusage.ru_majflt); + pid_incremental_rate(stat, p->cmajflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_majflt); + pid_incremental_rate(stat, p->utime, (kernel_uint_t)proc_info->ki_rusage.ru_utime.tv_sec * 100 + proc_info->ki_rusage.ru_utime.tv_usec / 10000); + pid_incremental_rate(stat, p->stime, (kernel_uint_t)proc_info->ki_rusage.ru_stime.tv_sec * 100 + proc_info->ki_rusage.ru_stime.tv_usec / 10000); + pid_incremental_rate(stat, p->cutime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_utime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_utime.tv_usec / 10000); + pid_incremental_rate(stat, p->cstime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_stime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_utime.tv_usec / 10000); + + p->num_threads = proc_info->ki_numthreads; if(enable_guest_charts) { - last = p->gtime_raw; - p->gtime_raw = str2ull(procfile_lineword(ff, 0, 42)); - p->gtime = (p->gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + enable_guest_charts = 0; + info("Guest charts aren't supported by FreeBSD"); + } +#else + pid_incremental_rate(stat, p->minflt, str2kernel_uint_t(procfile_lineword(ff, 0, 9))); + pid_incremental_rate(stat, p->cminflt, str2kernel_uint_t(procfile_lineword(ff, 0, 10))); + pid_incremental_rate(stat, p->majflt, str2kernel_uint_t(procfile_lineword(ff, 0, 11))); + pid_incremental_rate(stat, p->cmajflt, str2kernel_uint_t(procfile_lineword(ff, 0, 12))); + pid_incremental_rate(stat, p->utime, str2kernel_uint_t(procfile_lineword(ff, 0, 13))); + pid_incremental_rate(stat, p->stime, str2kernel_uint_t(procfile_lineword(ff, 0, 14))); + pid_incremental_rate(stat, p->cutime, str2kernel_uint_t(procfile_lineword(ff, 0, 15))); + pid_incremental_rate(stat, p->cstime, str2kernel_uint_t(procfile_lineword(ff, 0, 16))); + // p->priority = str2kernel_uint_t(procfile_lineword(ff, 0, 17)); + // p->nice = str2kernel_uint_t(procfile_lineword(ff, 0, 18)); + p->num_threads = (int32_t)str2uint32_t(procfile_lineword(ff, 0, 19)); + // p->itrealvalue = str2kernel_uint_t(procfile_lineword(ff, 0, 20)); + // p->starttime = str2kernel_uint_t(procfile_lineword(ff, 0, 21)); + // p->vsize = str2kernel_uint_t(procfile_lineword(ff, 0, 22)); + // p->rss = str2kernel_uint_t(procfile_lineword(ff, 0, 23)); + // p->rsslim = str2kernel_uint_t(procfile_lineword(ff, 0, 24)); + // p->starcode = str2kernel_uint_t(procfile_lineword(ff, 0, 25)); + // p->endcode = str2kernel_uint_t(procfile_lineword(ff, 0, 26)); + // p->startstack = str2kernel_uint_t(procfile_lineword(ff, 0, 27)); + // p->kstkesp = str2kernel_uint_t(procfile_lineword(ff, 0, 28)); + // p->kstkeip = str2kernel_uint_t(procfile_lineword(ff, 0, 29)); + // p->signal = str2kernel_uint_t(procfile_lineword(ff, 0, 30)); + // p->blocked = str2kernel_uint_t(procfile_lineword(ff, 0, 31)); + // p->sigignore = str2kernel_uint_t(procfile_lineword(ff, 0, 32)); + // p->sigcatch = str2kernel_uint_t(procfile_lineword(ff, 0, 33)); + // p->wchan = str2kernel_uint_t(procfile_lineword(ff, 0, 34)); + // p->nswap = str2kernel_uint_t(procfile_lineword(ff, 0, 35)); + // p->cnswap = str2kernel_uint_t(procfile_lineword(ff, 0, 36)); + // p->exit_signal = str2kernel_uint_t(procfile_lineword(ff, 0, 37)); + // p->processor = str2kernel_uint_t(procfile_lineword(ff, 0, 38)); + // p->rt_priority = str2kernel_uint_t(procfile_lineword(ff, 0, 39)); + // p->policy = str2kernel_uint_t(procfile_lineword(ff, 0, 40)); + // p->delayacct_blkio_ticks = str2kernel_uint_t(procfile_lineword(ff, 0, 41)); - last = p->cgtime_raw; - p->cgtime_raw = str2ull(procfile_lineword(ff, 0, 43)); - p->cgtime = (p->cgtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + if(enable_guest_charts) { + + pid_incremental_rate(stat, p->gtime, str2kernel_uint_t(procfile_lineword(ff, 0, 42))); + pid_incremental_rate(stat, p->cgtime, str2kernel_uint_t(procfile_lineword(ff, 0, 43))); if (show_guest_time || p->gtime || p->cgtime) { p->utime -= (p->utime >= p->gtime) ? p->gtime : p->utime; @@ -700,9 +1006,10 @@ static inline int read_proc_pid_stat(struct pid_stat *p) { show_guest_time = 1; } } +#endif 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", global_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); + fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d\n", netdata_configured_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; @@ -735,12 +1042,16 @@ cleanup: return 0; } -static inline int read_proc_pid_statm(struct pid_stat *p) { +static inline int read_proc_pid_statm(struct pid_stat *p, void *ptr) { + (void)ptr; +#ifdef __FreeBSD__ + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr; +#else static procfile *ff = NULL; if(unlikely(!p->statm_filename)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", global_host_prefix, p->pid); + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", netdata_configured_host_prefix, p->pid); p->statm_filename = strdupz(filename); } @@ -749,19 +1060,27 @@ static inline int read_proc_pid_statm(struct pid_stat *p) { ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif - file_counter++; + calls_counter++; - p->statm_size = str2ull(procfile_lineword(ff, 0, 0)); - p->statm_resident = str2ull(procfile_lineword(ff, 0, 1)); - p->statm_share = str2ull(procfile_lineword(ff, 0, 2)); - // p->statm_text = str2ull(procfile_lineword(ff, 0, 3)); - // p->statm_lib = str2ull(procfile_lineword(ff, 0, 4)); - // p->statm_data = str2ull(procfile_lineword(ff, 0, 5)); - // p->statm_dirty = str2ull(procfile_lineword(ff, 0, 6)); +#ifdef __FreeBSD__ + p->statm_size = proc_info->ki_size / sysconf(_SC_PAGESIZE); + p->statm_resident = proc_info->ki_rssize; + p->statm_share = 0; // do we have to use ru_ixrss here? +#else + p->statm_size = str2kernel_uint_t(procfile_lineword(ff, 0, 0)); + p->statm_resident = str2kernel_uint_t(procfile_lineword(ff, 0, 1)); + p->statm_share = str2kernel_uint_t(procfile_lineword(ff, 0, 2)); + // p->statm_text = str2kernel_uint_t(procfile_lineword(ff, 0, 3)); + // p->statm_lib = str2kernel_uint_t(procfile_lineword(ff, 0, 4)); + // p->statm_data = str2kernel_uint_t(procfile_lineword(ff, 0, 5)); + // p->statm_dirty = str2kernel_uint_t(procfile_lineword(ff, 0, 6)); +#endif return 1; +#ifndef __FreeBSD__ cleanup: p->statm_size = 0; p->statm_resident = 0; @@ -771,14 +1090,19 @@ cleanup: // p->statm_data = 0; // p->statm_dirty = 0; return 0; +#endif } -static inline int read_proc_pid_io(struct pid_stat *p) { +static inline int read_proc_pid_io(struct pid_stat *p, void *ptr) { + (void)ptr; +#ifdef __FreeBSD__ + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr; +#else static procfile *ff = NULL; if(unlikely(!p->io_filename)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", global_host_prefix, p->pid); + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", netdata_configured_host_prefix, p->pid); p->io_filename = strdupz(filename); } @@ -788,41 +1112,25 @@ static inline int read_proc_pid_io(struct pid_stat *p) { ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif - file_counter++; + calls_counter++; p->last_io_collected_usec = p->io_collected_usec; - p->io_collected_usec = now_realtime_usec(); - - unsigned long long last; - - last = p->io_logical_bytes_read_raw; - p->io_logical_bytes_read_raw = str2ull(procfile_lineword(ff, 0, 1)); - p->io_logical_bytes_read = (p->io_logical_bytes_read_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - last = p->io_logical_bytes_written_raw; - p->io_logical_bytes_written_raw = str2ull(procfile_lineword(ff, 1, 1)); - p->io_logical_bytes_written = (p->io_logical_bytes_written_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - // last = p->io_read_calls_raw; - // p->io_read_calls_raw = str2ull(procfile_lineword(ff, 2, 1)); - // p->io_read_calls = (p->io_read_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - // last = p->io_write_calls_raw; - // p->io_write_calls_raw = str2ull(procfile_lineword(ff, 3, 1)); - // p->io_write_calls = (p->io_write_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); + p->io_collected_usec = now_monotonic_usec(); - last = p->io_storage_bytes_read_raw; - p->io_storage_bytes_read_raw = str2ull(procfile_lineword(ff, 4, 1)); - p->io_storage_bytes_read = (p->io_storage_bytes_read_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - last = p->io_storage_bytes_written_raw; - p->io_storage_bytes_written_raw = str2ull(procfile_lineword(ff, 5, 1)); - p->io_storage_bytes_written = (p->io_storage_bytes_written_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - // last = p->io_cancelled_write_bytes_raw; - // p->io_cancelled_write_bytes_raw = str2ull(procfile_lineword(ff, 6, 1)); - // p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); +#ifdef __FreeBSD__ + pid_incremental_rate(io, p->io_storage_bytes_read, proc_info->ki_rusage.ru_inblock); + pid_incremental_rate(io, p->io_storage_bytes_written, proc_info->ki_rusage.ru_oublock); +#else + pid_incremental_rate(io, p->io_logical_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 0, 1))); + pid_incremental_rate(io, p->io_logical_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 1, 1))); + // pid_incremental_rate(io, p->io_read_calls, str2kernel_uint_t(procfile_lineword(ff, 2, 1))); + // pid_incremental_rate(io, p->io_write_calls, str2kernel_uint_t(procfile_lineword(ff, 3, 1))); + pid_incremental_rate(io, p->io_storage_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 4, 1))); + pid_incremental_rate(io, p->io_storage_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 5, 1))); + // pid_incremental_rate(io, p->io_cancelled_write_bytes, str2kernel_uint_t(procfile_lineword(ff, 6, 1))); +#endif if(unlikely(global_iterations_counter == 1)) { p->io_logical_bytes_read = 0; @@ -836,6 +1144,7 @@ static inline int read_proc_pid_io(struct pid_stat *p) { return 1; +#ifndef __FreeBSD__ cleanup: p->io_logical_bytes_read = 0; p->io_logical_bytes_written = 0; @@ -845,60 +1154,77 @@ cleanup: p->io_storage_bytes_written = 0; // p->io_cancelled_write_bytes = 0; return 0; +#endif } -unsigned long long global_utime = 0; -unsigned long long global_stime = 0; -unsigned long long global_gtime = 0; - static inline int read_proc_stat() { +#ifdef __FreeBSD__ + long cp_time[CPUSTATES]; + static kernel_uint_t utime_raw = 0, stime_raw = 0, ntime_raw = 0; + + if (unlikely(CPUSTATES != 5)) { + error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES); + goto cleanup; + } + if (unlikely(GETSYSCTL_BY_NAME("kern.cp_time", cp_time))) goto cleanup; +#else 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; + static kernel_uint_t utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0; +#endif static usec_t collected_usec = 0, last_collected_usec = 0; +#ifndef __FreeBSD__ if(unlikely(!ff)) { - snprintfz(filename, FILENAME_MAX, "%s/proc/stat", global_host_prefix); + snprintfz(filename, FILENAME_MAX, "%s/proc/stat", netdata_configured_host_prefix); ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) goto cleanup; } ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif last_collected_usec = collected_usec; - collected_usec = now_realtime_usec(); - - file_counter++; + collected_usec = now_monotonic_usec(); - unsigned long long last; + calls_counter++; - last = utime_raw; - utime_raw = str2ull(procfile_lineword(ff, 0, 1)); - global_utime = (utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); + // temporary - it is added global_ntime; + kernel_uint_t global_ntime = 0; - // nice time, on user time - last = ntime_raw; - ntime_raw = str2ull(procfile_lineword(ff, 0, 2)); - global_utime += (ntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); - - last = stime_raw; - stime_raw = str2ull(procfile_lineword(ff, 0, 3)); - global_stime = (stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); +#ifdef __FreeBSD__ + incremental_rate(global_utime, utime_raw, cp_time[0], collected_usec, last_collected_usec); + incremental_rate(global_ntime, ntime_raw, cp_time[1], collected_usec, last_collected_usec); + incremental_rate(global_stime, stime_raw, cp_time[2], collected_usec, last_collected_usec); +#else + incremental_rate(global_utime, utime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 1)), collected_usec, last_collected_usec); + incremental_rate(global_ntime, ntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 2)), collected_usec, last_collected_usec); + incremental_rate(global_stime, stime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 3)), collected_usec, last_collected_usec); + incremental_rate(global_gtime, gtime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 10)), collected_usec, last_collected_usec); +#endif - last = gtime_raw; - gtime_raw = str2ull(procfile_lineword(ff, 0, 10)); - global_gtime = (gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); + global_utime += global_ntime; +#ifdef __FreeBSD__ + if(enable_guest_charts) { + enable_guest_charts = 0; + info("Guest charts aren't supported by FreeBSD"); + } +#else if(enable_guest_charts) { + // temporary - it is added global_ntime; + kernel_uint_t global_gntime = 0; + // guest nice time, on guest time - last = gntime_raw; - gntime_raw = str2ull(procfile_lineword(ff, 0, 11)); - global_gtime += (gntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); + incremental_rate(global_gntime, gntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 11)), collected_usec, last_collected_usec); + + global_gtime += global_gntime; // remove guest time from user time global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime; } +#endif if(unlikely(global_iterations_counter == 1)) { global_utime = 0; @@ -917,26 +1243,6 @@ cleanup: // ---------------------------------------------------------------------------- -// file descriptor -// this is used to keep a global list of all open files of the system -// it is needed in order to calculate the unique files processes have open - -#define FILE_DESCRIPTORS_INCREASE_STEP 100 - -struct file_descriptor { - avl avl; -#ifdef NETDATA_INTERNAL_CHECKS - uint32_t magic; -#endif /* NETDATA_INTERNAL_CHECKS */ - uint32_t hash; - const char *name; - int type; - int count; - int pos; -} *all_files = NULL; - -int all_files_len = 0; -int all_files_size = 0; int file_descriptor_compare(void* a, void* b) { #ifdef NETDATA_INTERNAL_CHECKS @@ -977,15 +1283,7 @@ static struct file_descriptor *file_descriptor_find(const char *name, uint32_t h #define file_descriptor_add(fd) avl_insert(&all_files_index, (avl *)(fd)) #define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl *)(fd)) -#define FILETYPE_OTHER 0 -#define FILETYPE_FILE 1 -#define FILETYPE_PIPE 2 -#define FILETYPE_SOCKET 3 -#define FILETYPE_INOTIFY 4 -#define FILETYPE_EVENTFD 5 -#define FILETYPE_EVENTPOLL 6 -#define FILETYPE_TIMERFD 7 -#define FILETYPE_SIGNALFD 8 +// ---------------------------------------------------------------------------- static inline void file_descriptor_not_used(int id) { @@ -1066,7 +1364,7 @@ static inline void all_files_grow() { all_files_size += FILE_DESCRIPTORS_INCREASE_STEP; } -static inline int file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, int type) { +static inline int file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, FD_FILETYPE type) { // check we have enough memory to add it if(!all_files || all_files_len == all_files_size) all_files_grow(); @@ -1147,21 +1445,26 @@ static inline int file_descriptor_find_or_add(const char *name) } // not found - 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); + FD_FILETYPE type; + if(likely(name[0] == '/')) type = FILETYPE_FILE; + else if(likely(strncmp(name, "pipe:", 5) == 0)) type = FILETYPE_PIPE; + else if(likely(strncmp(name, "socket:", 7) == 0)) type = FILETYPE_SOCKET; + else if(likely(strncmp(name, "anon_inode:", 11) == 0)) { + const char *t = &name[11]; + + if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY; + else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD; + else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL; + else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD; + else if(strcmp(t, "[signalfd]") == 0) type = FILETYPE_SIGNALFD; + else { + if(unlikely(debug)) + fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name); - type = FILETYPE_OTHER; + type = FILETYPE_OTHER; + } } + else if(likely(strcmp(name, "inotify") == 0)) type = FILETYPE_INOTIFY; else { if(unlikely(debug)) fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name); @@ -1172,81 +1475,243 @@ static inline int file_descriptor_find_or_add(const char *name) return file_descriptor_set_on_empty_slot(name, hash, type); } -static inline int read_pid_file_descriptors(struct pid_stat *p) { - char dirname[FILENAME_MAX+1]; +static inline void make_all_pid_fds_negative(struct pid_stat *p) { + int *fd = p->fds, *end = &p->fds[p->fds_size]; + while(fd < end) { + *fd = -(*fd); + fd++; + } +} - snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", global_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]; +static inline void cleanup_negative_pid_fds(struct pid_stat *p) { + int *fd = p->fds, *fdend = &p->fds[p->fds_size]; - // make the array negative - for(c = 0 ; c < p->fds_size ; c++) - p->fds[c] = -p->fds[c]; + while(fd < fdend) { + if(unlikely(*fd < 0)) { + file_descriptor_not_used(-(*fd)); + *fd++ = 0; + } + else + fd++; + } +} - while((de = readdir(fds))) { - if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; +static inline void zero_pid_fds(struct pid_stat *p, int first, int size) { + int *fd = &p->fds[first], *end = &p->fds[first + size]; + while(fd < end) *fd++ = 0; +} - // check if the fds array is small - int fdid = (int)str2l(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 + MAX_SPARE_FDS); +static inline int read_pid_file_descriptors(struct pid_stat *p, void *ptr) { + (void)ptr; +#ifdef __FreeBSD__ + int mib[4]; + size_t size; + struct kinfo_file *fds; + static char *fdsbuf; + char *bfdsbuf, *efdsbuf; + char fdsname[FILENAME_MAX + 1]; + + // we make all pid fds negative, so that + // we can detect unused file descriptors + // at the end, to free them + make_all_pid_fds_negative(p); + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_FILEDESC; + mib[3] = p->pid; + + if (unlikely(sysctl(mib, 4, NULL, &size, NULL, 0))) { + error("sysctl error: Can't get file descriptors data size for pid %d", p->pid); + return 0; + } + if (likely(size > 0)) + fdsbuf = reallocz(fdsbuf, size); + if (unlikely(sysctl(mib, 4, fdsbuf, &size, NULL, 0))) { + error("sysctl error: Can't get file descriptors data for pid %d", p->pid); + return 0; + } - p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int)); + bfdsbuf = fdsbuf; + efdsbuf = fdsbuf + size; + while (bfdsbuf < efdsbuf) { + fds = (struct kinfo_file *)(uintptr_t)bfdsbuf; + if (unlikely(fds->kf_structsize == 0)) + break; - // and initialize it - for(c = p->fds_size ; c < (fdid + MAX_SPARE_FDS) ; c++) p->fds[c] = 0; - p->fds_size = fdid + MAX_SPARE_FDS; - } + // do not process file descriptors for current working directory, root directory, + // jail directory, ktrace vnode, text vnode and controlling terminal + if (unlikely(fds->kf_fd < 0)) { + bfdsbuf += fds->kf_structsize; + continue; + } - if(p->fds[fdid] == 0) { - // we don't know this fd, get it + // get file descriptors array index + int fdid = fds->kf_fd; - sprintf(fdname, "%s/proc/%d/fd/%s", global_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++; + // check if the fds array is small + if (unlikely(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 + MAX_SPARE_FDS); + + p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int)); - // if another process already has this, we will get - // the same id - p->fds[fdid] = file_descriptor_find_or_add(linkname); + // and initialize it + zero_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size); + p->fds_size = fdid + MAX_SPARE_FDS; + } + + if (unlikely(p->fds[fdid] == 0)) { + // we don't know this fd, get it + + switch (fds->kf_type) { + case KF_TYPE_FIFO: + case KF_TYPE_VNODE: + if (unlikely(!fds->kf_path[0])) { + sprintf(fdsname, "other: inode: %lu", fds->kf_un.kf_file.kf_file_fileid); + break; + } + sprintf(fdsname, "%s", fds->kf_path); + break; + case KF_TYPE_SOCKET: + switch (fds->kf_sock_domain) { + case AF_INET: + case AF_INET6: + if (fds->kf_sock_protocol == IPPROTO_TCP) + sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_inpcb); + else + sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_pcb); + break; + case AF_UNIX: + /* print address of pcb and connected pcb */ + sprintf(fdsname, "socket: %lx %lx", fds->kf_un.kf_sock.kf_sock_pcb, fds->kf_un.kf_sock.kf_sock_unpconn); + break; + default: + /* print protocol number and socket address */ + sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_sa_local.__ss_pad1, fds->kf_sa_local.__ss_pad2); + } + break; + case KF_TYPE_PIPE: + sprintf(fdsname, "pipe: %lu %lu", fds->kf_un.kf_pipe.kf_pipe_addr, fds->kf_un.kf_pipe.kf_pipe_peer); + break; + case KF_TYPE_PTS: + sprintf(fdsname, "other: pts: %u", fds->kf_un.kf_pts.kf_pts_dev); + break; + case KF_TYPE_SHM: + sprintf(fdsname, "other: shm: %s size: %lu", fds->kf_path, fds->kf_un.kf_file.kf_file_size); + break; + case KF_TYPE_SEM: + sprintf(fdsname, "other: sem: %u", fds->kf_un.kf_sem.kf_sem_value); + break; + default: + sprintf(fdsname, "other: pid: %d fd: %d", fds->kf_un.kf_proc.kf_pid, fds->kf_fd); } + // if another process already has this, we will get + // the same id + p->fds[fdid] = file_descriptor_find_or_add(fdsname); + } + // 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]; + + else + p->fds[fdid] = -p->fds[fdid]; + + bfdsbuf += fds->kf_structsize; + } +#else + if(unlikely(!p->fds_dirname)) { + char dirname[FILENAME_MAX+1]; + snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", netdata_configured_host_prefix, p->pid); + p->fds_dirname = strdupz(dirname); + } + + DIR *fds = opendir(p->fds_dirname); + if(unlikely(!fds)) return 0; + + struct dirent *de; + char fdname[FILENAME_MAX + 1]; + char linkname[FILENAME_MAX + 1]; + + // we make all pid fds negative, so that + // we can detect unused file descriptors + // at the end, to free them + make_all_pid_fds_negative(p); + + while((de = readdir(fds))) { + // we need only files with numeric names + + if(unlikely(de->d_name[0] < '0' || de->d_name[0] > '9')) + continue; + + // get its number + int fdid = (int) str2l(de->d_name); + if(unlikely(fdid < 0)) continue; + + // check if the fds array is small + if(unlikely(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 + MAX_SPARE_FDS + ); + + p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int)); + + // and initialize it + zero_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size); + p->fds_size = fdid + MAX_SPARE_FDS; } - 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; + if(unlikely(p->fds[fdid] == 0)) { + // we don't know this fd, get it + + sprintf(fdname, "%s/proc/%d/fd/%s", netdata_configured_host_prefix, p->pid, de->d_name); + ssize_t l = readlink(fdname, linkname, FILENAME_MAX); + if(unlikely(l == -1)) { + if(debug || (p->target && p->target->debug)) { + if(debug || (p->target && p->target->debug)) + error("Cannot read link %s", fdname); + } + continue; + } + else + 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 + // UPDATE: no we cannot use inodes - under /proc inodes don't change when the link is changed + + else + p->fds[fdid] = -p->fds[fdid]; } - else return 0; + + closedir(fds); +#endif + cleanup_negative_pid_fds(p); return 1; } // ---------------------------------------------------------------------------- -static inline int print_process_and_parents(struct pid_stat *p, unsigned long long time) { +static inline int print_process_and_parents(struct pid_stat *p, usec_t time) { char *prefix = "\\_ "; int indent = 0; @@ -1261,25 +1726,25 @@ static inline int print_process_and_parents(struct pid_stat *p, unsigned long lo for(i = 0; i < indent ;i++) buffer[i] = ' '; buffer[i] = '\0'; - fprintf(stderr, " %s %s%s (%d %s %lld" + fprintf(stderr, " %s %s%s (%d %s %llu" , buffer , prefix , p->comm , p->pid , p->updated?"running":"exited" - , (long long)p->stat_collected_usec - (long long)time + , p->stat_collected_usec - 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); + if(p->utime) fprintf(stderr, " utime=" KERNEL_UINT_FORMAT, p->utime); + if(p->stime) fprintf(stderr, " stime=" KERNEL_UINT_FORMAT, p->stime); + if(p->gtime) fprintf(stderr, " gtime=" KERNEL_UINT_FORMAT, p->gtime); + if(p->cutime) fprintf(stderr, " cutime=" KERNEL_UINT_FORMAT, p->cutime); + if(p->cstime) fprintf(stderr, " cstime=" KERNEL_UINT_FORMAT, p->cstime); + if(p->cgtime) fprintf(stderr, " cgtime=" KERNEL_UINT_FORMAT, p->cgtime); + if(p->minflt) fprintf(stderr, " minflt=" KERNEL_UINT_FORMAT, p->minflt); + if(p->cminflt) fprintf(stderr, " cminflt=" KERNEL_UINT_FORMAT, p->cminflt); + if(p->majflt) fprintf(stderr, " majflt=" KERNEL_UINT_FORMAT, p->majflt); + if(p->cmajflt) fprintf(stderr, " cmajflt=" KERNEL_UINT_FORMAT, p->cmajflt); fprintf(stderr, ")\n"); return indent + 1; @@ -1291,7 +1756,7 @@ static inline void print_process_tree(struct pid_stat *p, char *msg) { print_process_and_parents(p, p->stat_collected_usec); } -static inline void find_lost_child_debug(struct pid_stat *pe, unsigned long long lost, int type) { +static inline void find_lost_child_debug(struct pid_stat *pe, kernel_uint_t lost, int type) { int found = 0; struct pid_stat *p = NULL; @@ -1301,35 +1766,35 @@ static inline void find_lost_child_debug(struct pid_stat *pe, unsigned long long 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); + fprintf(stderr, " > process %d (%s) could use the lost exited child minflt " KERNEL_UINT_FORMAT " 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); + fprintf(stderr, " > process %d (%s) could use the lost exited child majflt " KERNEL_UINT_FORMAT " 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); + fprintf(stderr, " > process %d (%s) could use the lost exited child utime " KERNEL_UINT_FORMAT " 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); + fprintf(stderr, " > process %d (%s) could use the lost exited child stime " KERNEL_UINT_FORMAT " 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); + fprintf(stderr, " > process %d (%s) could use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm); found++; } break; @@ -1339,30 +1804,30 @@ static inline void find_lost_child_debug(struct pid_stat *pe, unsigned long long 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); + fprintf(stderr, " > cannot find any process to use the lost exited child minflt " KERNEL_UINT_FORMAT " 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); + fprintf(stderr, " > cannot find any process to use the lost exited child majflt " KERNEL_UINT_FORMAT " 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); + fprintf(stderr, " > cannot find any process to use the lost exited child utime " KERNEL_UINT_FORMAT " 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); + fprintf(stderr, " > cannot find any process to use the lost exited child stime " KERNEL_UINT_FORMAT " 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); + fprintf(stderr, " > cannot find any process to use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm); break; } } } -static inline unsigned long long remove_exited_child_from_parent(unsigned long long *field, unsigned long long *pfield) { - unsigned long long absorbed = 0; +static inline kernel_uint_t remove_exited_child_from_parent(kernel_uint_t *field, kernel_uint_t *pfield) { + kernel_uint_t absorbed = 0; if(*field > *pfield) { absorbed += *pfield; @@ -1385,20 +1850,18 @@ static inline void process_exited_processes() { 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); + kernel_uint_t utime = (p->utime_raw + p->cutime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + kernel_uint_t stime = (p->stime_raw + p->cstime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + kernel_uint_t gtime = (p->gtime_raw + p->cgtime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + kernel_uint_t minflt = (p->minflt_raw + p->cminflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + kernel_uint_t majflt = (p->majflt_raw + p->cmajflt_raw) * (USEC_PER_SEC * 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" + fprintf(stderr, "Absorb %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")\n" , p->comm , p->pid , p->updated?"running":"exited" @@ -1411,29 +1874,30 @@ static inline void process_exited_processes() { print_process_tree(p, "Searching parents"); } + struct pid_stat *pp; for(pp = p->parent; pp ; pp = pp->parent) { if(!pp->updated) continue; - unsigned long long absorbed; + kernel_uint_t 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); + fprintf(stderr, " > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " utime (remaining: " KERNEL_UINT_FORMAT ")\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); + fprintf(stderr, " > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " stime (remaining: " KERNEL_UINT_FORMAT ")\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, stime); absorbed = remove_exited_child_from_parent(>ime, &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); + fprintf(stderr, " > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " gtime (remaining: " KERNEL_UINT_FORMAT ")\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); + fprintf(stderr, " > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " minflt (remaining: " KERNEL_UINT_FORMAT ")\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); + fprintf(stderr, " > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " majflt (remaining: " KERNEL_UINT_FORMAT ")\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, majflt); } if(unlikely(utime + stime + gtime + minflt + majflt > 0)) { @@ -1448,7 +1912,7 @@ static inline void process_exited_processes() { 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" + fprintf(stderr, " > remaining resources - KEEP - for another loop: %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")\n" , p->comm , p->pid , p->updated?"running":"exited" @@ -1513,7 +1977,7 @@ static inline void link_all_processes_to_their_parents(void) { 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); + fprintf(stderr, "apps.plugin: \tchild %d (%s, %s) on target '%s' has parent %d (%s, %s). Parent: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", gtime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", cgtime=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT "\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; @@ -1540,6 +2004,7 @@ static inline void link_all_processes_to_their_parents(void) { // to avoid filling up all disk space // if debug is enabled, all errors are printed +#ifndef __FreeBSD__ static int compar_pid(const void *pid1, const void *pid2) { struct pid_stat *p1 = all_pids[*((pid_t *)pid1)]; @@ -1550,53 +2015,11 @@ static int compar_pid(const void *pid1, const void *pid2) { else return 1; } +#endif -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')", global_host_prefix, p->pid, p->comm); - break; - - case PID_LOG_STATM: - error("Cannot process %s/proc/%d/statm (command '%s')", global_host_prefix, p->pid, p->comm); - break; - - case PID_LOG_CMDLINE: - error("Cannot process %s/proc/%d/cmdline (command '%s')", global_host_prefix, p->pid, p->comm); - break; - - case PID_LOG_FDS: - error("Cannot process entries in %s/proc/%d/fd (command '%s')", global_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; -} - -static inline int 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); +static inline int collect_data_for_pid(pid_t pid, void *ptr) { + if(unlikely(pid < INIT_PID || pid > pid_max)) { + error("Invalid pid %d read (expected %d to %d). Ignoring process.", pid, INIT_PID, pid_max); return 0; } @@ -1609,11 +2032,11 @@ static inline int collect_data_for_pid(pid_t pid) { // -------------------------------------------------------------------- // /proc/<pid>/stat - if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p)))) + if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p, ptr)))) // there is no reason to proceed if we cannot get its status return 0; - read_proc_pid_ownership(p); + read_proc_pid_ownership(p, ptr); // check its parent pid if(unlikely(p->ppid < 0 || p->ppid > pid_max)) { @@ -1624,61 +2047,20 @@ static inline int collect_data_for_pid(pid_t pid) { // -------------------------------------------------------------------- // /proc/<pid>/io - managed_log(p, PID_LOG_IO, read_proc_pid_io(p)); + managed_log(p, PID_LOG_IO, read_proc_pid_io(p, ptr)); // -------------------------------------------------------------------- // /proc/<pid>/statm - if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p)))) + if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p, ptr)))) // there is no reason to proceed if we cannot get its memory status return 0; // -------------------------------------------------------------------- - // 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)); + managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p, ptr)); // -------------------------------------------------------------------- // done! @@ -1694,71 +2076,116 @@ static inline int collect_data_for_pid(pid_t pid) { return 1; } -static int collect_data_for_all_processes_from_proc(void) { +static int collect_data_for_all_processes(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 +#ifdef __FreeBSD__ + int i, procnum; + size_t procbase_size; + static struct kinfo_proc *procbase; - long slc = 0; + int mib[3]; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PROC; + if (unlikely(sysctl(mib, 3, NULL, &procbase_size, NULL, 0))) { + error("sysctl error: Can't get processes data size"); + return 0; + } + procbase = reallocz(procbase, procbase_size); + if (unlikely(sysctl(mib, 3, procbase, &procbase_size, NULL, 0))) { + error("sysctl error: Can't get processes data"); + return 0; + } + procnum = procbase_size / sizeof(struct kinfo_proc); +#endif + + if(all_pids_count) { +#ifndef __FreeBSD__ + size_t slc = 0; +#endif for(p = root_of_pids; p ; p = p->next) { - p->read = 0; + p->read = 0; // mark it as not read, so that collect_data_for_pid() will read it p->updated = 0; - p->new_entry = 0; p->merged = 0; p->children_count = 0; p->parent = NULL; +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) all_pids_sortlist[slc++] = p->pid; +#endif } +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) 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); + error("Internal error: I was thinking I had %zu processes in my arrays, but it seems there are more.", all_pids_count); all_pids_count = slc; } if(include_exited_childs) { + // 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. + qsort((void *)all_pids_sortlist, (size_t)all_pids_count, sizeof(pid_t), compar_pid); + + // we forward read all running processes + // collect_data_for_pid() is smart enough, + // not to read the same pid twice per iteration for(slc = 0; slc < all_pids_count; slc++) - collect_data_for_pid(all_pids_sortlist[slc]); + collect_data_for_pid(all_pids_sortlist[slc], NULL); } +#endif } +#ifdef __FreeBSD__ + for (i = INIT_PID; i < procnum - INIT_PID; ++i) { + pid_t pid = procbase[i].ki_pid; + collect_data_for_pid(pid, &procbase[i]); + } +#else char dirname[FILENAME_MAX + 1]; - snprintfz(dirname, FILENAME_MAX, "%s/proc", global_host_prefix); + snprintfz(dirname, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix); DIR *dir = opendir(dirname); if(!dir) return 0; - struct dirent *file = NULL; + struct dirent *de = NULL; + + while((de = readdir(dir))) { + char *endptr = de->d_name; + + if(unlikely(de->d_type != DT_DIR || de->d_name[0] < '0' || de->d_name[0] > '9')) + continue; - while((file = readdir(dir))) { - char *endptr = file->d_name; - pid_t pid = (pid_t) strtoul(file->d_name, &endptr, 10); + pid_t pid = (pid_t) strtoul(de->d_name, &endptr, 10); // make sure we read a valid number - if(unlikely(endptr == file->d_name || *endptr != '\0')) + if(unlikely(endptr == de->d_name || *endptr != '\0')) continue; - collect_data_for_pid(pid); + collect_data_for_pid(pid, NULL); } closedir(dir); +#endif if(!all_pids_count) return 0; + // we need /proc/stat to normalize the cpu consumption of the exited childs + read_proc_stat(); + + // build the process tree + link_all_processes_to_their_parents(); + // 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; @@ -1786,15 +2213,14 @@ static void cleanup_exited_pids(void) { 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; - } + 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; @@ -1857,7 +2283,7 @@ static void apply_apps_groups_targets_inheritance(void) { && p->parent && p->parent->children_count && (p->target == p->parent->target || !p->parent->target) - && p->ppid != 1 + && p->ppid != INIT_PID )) { p->parent->children_count--; p->merged = 1; @@ -1879,8 +2305,8 @@ static void apply_apps_groups_targets_inheritance(void) { } // init goes always to default target - if(all_pids[1]) - all_pids[1]->target = apps_groups_default_target; + if(all_pids[INIT_PID]) + all_pids[INIT_PID]->target = apps_groups_default_target; // give a default target on all top level processes if(unlikely(debug)) loops++; @@ -1918,9 +2344,9 @@ static void apply_apps_groups_targets_inheritance(void) { fprintf(stderr, "apps.plugin: apply_apps_groups_targets_inheritance() made %d loops on the process tree\n", loops); } -static long zero_all_targets(struct target *root) { +static size_t zero_all_targets(struct target *root) { struct target *w; - long count = 0; + size_t count = 0; for (w = root; w ; w = w->next) { count++; @@ -2032,7 +2458,7 @@ static inline void aggregate_fd_on_target(int fd, struct target *w) { w->openeventpolls++; break; - default: + case FILETYPE_OTHER: w->openother++; break; } @@ -2111,7 +2537,7 @@ static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p, w->num_threads += p->num_threads; 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); + fprintf(stderr, "apps.plugin: \taggregating '%s' pid %d on target '%s' utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", gtime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", cgtime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT "\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); } static void calculate_netdata_statistics(void) { @@ -2120,7 +2546,7 @@ static void calculate_netdata_statistics(void) { zero_all_targets(users_root_target); zero_all_targets(groups_root_target); - apps_groups_targets = zero_all_targets(apps_groups_root_target); + apps_groups_targets_count = zero_all_targets(apps_groups_root_target); // this has to be done, before the cleanup struct pid_stat *p = NULL; @@ -2182,21 +2608,18 @@ static void calculate_netdata_statistics(void) { 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) { +static inline void send_BEGIN(const char *type, const char *id, usec_t usec) { fprintf(stdout, "BEGIN %s.%s %llu\n", type, id, usec); } -static inline void send_SET(const char *name, unsigned long long value) { - fprintf(stdout, "SET %s = %llu\n", name, value); +static inline void send_SET(const char *name, kernel_uint_t value) { + fprintf(stdout, "SET %s = " KERNEL_UINT_FORMAT "\n", name, value); } static inline void send_END(void) { 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; - static usec_t send_resource_usage_to_netdata() { static struct timeval last = { 0, 0 }; static struct rusage me_last; @@ -2209,7 +2632,7 @@ static usec_t send_resource_usage_to_netdata() { usec_t cpusyst; if(!last.tv_sec) { - now_realtime_timeval(&last); + now_monotonic_timeval(&last); getrusage(RUSAGE_SELF, &me_last); // the first time, give a zero to allow @@ -2220,7 +2643,7 @@ static usec_t send_resource_usage_to_netdata() { cpusyst = 0; } else { - now_realtime_timeval(&now); + now_monotonic_timeval(&now); getrusage(RUSAGE_SELF, &me); usec = dt_usec(&now, &last); @@ -2231,73 +2654,115 @@ static usec_t send_resource_usage_to_netdata() { memmove(&me_last, &me, sizeof(struct rusage)); } + static char created_charts = 0; + if(unlikely(!created_charts)) { + created_charts = 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_sizes '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_sizes line 140001 %1$d\n" + "DIMENSION calls '' incremental 1 1\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" + "DIMENSION new_pids 'new pids' incremental 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) + fprintf(stdout + , "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 + ); + } + fprintf(stdout, "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" + "BEGIN netdata.apps_sizes %llu\n" + "SET calls = %zu\n" + "SET files = %zu\n" + "SET pids = %zu\n" "SET fds = %d\n" - "SET targets = %ld\n" + "SET targets = %zu\n" + "SET new_pids = %zu\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" + "SET utime = %u\n" + "SET stime = %u\n" + "SET gtime = %u\n" + "SET minflt = %u\n" + "SET majflt = %u\n" "END\n" , usec , cpuuser , cpusyst , usec + , calls_counter , file_counter , all_pids_count , all_files_len - , apps_groups_targets + , apps_groups_targets_count + , targets_assignment_counter , 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) + , (unsigned int)(utime_fix_ratio * 100 * RATES_DETAIL) + , (unsigned int)(stime_fix_ratio * 100 * RATES_DETAIL) + , (unsigned int)(gtime_fix_ratio * 100 * RATES_DETAIL) + , (unsigned int)(minflt_fix_ratio * 100 * RATES_DETAIL) + , (unsigned int)(majflt_fix_ratio * 100 * RATES_DETAIL) ); if(include_exited_childs) fprintf(stdout, "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" + "SET cutime = %u\n" + "SET cstime = %u\n" + "SET cgtime = %u\n" + "SET cminflt = %u\n" + "SET cmajflt = %u\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) + , (unsigned int)(cutime_fix_ratio * 100 * RATES_DETAIL) + , (unsigned int)(cstime_fix_ratio * 100 * RATES_DETAIL) + , (unsigned int)(cgtime_fix_ratio * 100 * RATES_DETAIL) + , (unsigned int)(cminflt_fix_ratio * 100 * RATES_DETAIL) + , (unsigned int)(cmajflt_fix_ratio * 100 * RATES_DETAIL) ); return usec; } -static void normalize_data(struct target *root) { +static void normalize_utilization(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; + kernel_uint_t max_time = processors * hz * RATES_DETAIL; + kernel_uint_t 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; + if(global_utime > max_time) global_utime = max_time; + if(global_stime > max_time) global_stime = max_time; + if(global_gtime > max_time) global_gtime = max_time; for(w = root; w ; w = w->next) { if(w->target || (!w->processes && !w->exposed)) continue; @@ -2397,11 +2862,11 @@ static void normalize_data(struct target *root) { 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 " + "SYSTEM: u=" KERNEL_UINT_FORMAT " s=" KERNEL_UINT_FORMAT " g=" KERNEL_UINT_FORMAT " " + "COLLECTED: u=" KERNEL_UINT_FORMAT " s=" KERNEL_UINT_FORMAT " g=" KERNEL_UINT_FORMAT " cu=" KERNEL_UINT_FORMAT " cs=" KERNEL_UINT_FORMAT " cg=" KERNEL_UINT_FORMAT " " + "DELTA: u=" KERNEL_UINT_FORMAT " s=" KERNEL_UINT_FORMAT " g=" KERNEL_UINT_FORMAT " " "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 " + "FINALLY: u=" KERNEL_UINT_FORMAT " s=" KERNEL_UINT_FORMAT " g=" KERNEL_UINT_FORMAT " cu=" KERNEL_UINT_FORMAT " cs=" KERNEL_UINT_FORMAT " cg=" KERNEL_UINT_FORMAT " " "\n" , global_utime , global_stime @@ -2412,21 +2877,21 @@ static void normalize_data(struct target *root) { , 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 + cutime - global_utime + , stime + cstime - global_stime + , gtime + cgtime - 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) + , (kernel_uint_t)(utime * utime_fix_ratio) + , (kernel_uint_t)(stime * stime_fix_ratio) + , (kernel_uint_t)(gtime * gtime_fix_ratio) + , (kernel_uint_t)(cutime * cutime_fix_ratio) + , (kernel_uint_t)(cstime * cstime_fix_ratio) + , (kernel_uint_t)(cgtime * cgtime_fix_ratio) ); } } @@ -2437,21 +2902,21 @@ static void send_collected_data_to_netdata(struct target *root, const char *type 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_SET(w->name, (kernel_uint_t)(w->utime * utime_fix_ratio) + (kernel_uint_t)(w->stime * stime_fix_ratio) + (kernel_uint_t)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((kernel_uint_t)(w->cutime * cutime_fix_ratio) + (kernel_uint_t)(w->cstime * cstime_fix_ratio) + (kernel_uint_t)(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_SET(w->name, (kernel_uint_t)(w->utime * utime_fix_ratio) + (include_exited_childs?((kernel_uint_t)(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_SET(w->name, (kernel_uint_t)(w->stime * stime_fix_ratio) + (include_exited_childs?((kernel_uint_t)(w->cstime * cstime_fix_ratio)):0ULL)); } send_END(); @@ -2459,7 +2924,7 @@ static void send_collected_data_to_netdata(struct target *root, const char *type 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_SET(w->name, (kernel_uint_t)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((kernel_uint_t)(w->cgtime * cgtime_fix_ratio)):0ULL)); } send_END(); } @@ -2495,17 +2960,18 @@ static void send_collected_data_to_netdata(struct target *root, const char *type 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_SET(w->name, (kernel_uint_t)(w->minflt * minflt_fix_ratio) + (include_exited_childs?((kernel_uint_t)(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_SET(w->name, (kernel_uint_t)(w->majflt * majflt_fix_ratio) + (include_exited_childs?((kernel_uint_t)(w->cmajflt * cmajflt_fix_ratio)):0ULL)); } send_END(); +#ifndef __FreeBSD__ send_BEGIN(type, "lreads", usec); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) @@ -2519,6 +2985,7 @@ static void send_collected_data_to_netdata(struct target *root, const char *type send_SET(w->name, w->io_logical_bytes_written); } send_END(); +#endif send_BEGIN(type, "preads", usec); for (w = root; w ; w = w->next) { @@ -2644,29 +3111,43 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); } - fprintf(stdout, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every); +#ifdef __FreeBSD__ + fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'blocks/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every); + for (w = root; w ; w = w->next) { + if(unlikely(w->exposed)) + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); + } + + fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'blocks/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every); + for (w = root; w ; w = w->next) { + if(unlikely(w->exposed)) + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); + } +#else + 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(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } - fprintf(stdout, "CHART %s.lwrites '' '%s I/O Logical Writes' 'kilobytes/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every); + 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(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } - fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every); + 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(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } - fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'kilobytes/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every); + 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(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } +#endif if(enable_file_charts) { fprintf(stdout, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type, @@ -2696,6 +3177,22 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type // ---------------------------------------------------------------------------- // parse command line arguments +int check_proc_1_io() { + int ret = 0; + + procfile *ff = procfile_open("/proc/1/io", NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO); + if(!ff) goto cleanup; + + ff = procfile_readall(ff); + if(!ff) goto cleanup; + + ret = 1; + +cleanup: + procfile_close(ff); + return ret; +} + static void parse_args(int argc, char **argv) { int i, freq = 0; @@ -2710,11 +3207,20 @@ static void parse_args(int argc, char **argv) } } - if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0) { + if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) { printf("apps.plugin %s\n", VERSION); exit(0); } + if(strcmp("test-permissions", argv[i]) == 0 || strcmp("-t", argv[i]) == 0) { + if(!check_proc_1_io()) { + perror("Tried to read /proc/1/io and it failed"); + exit(1); + } + printf("OK\n"); + exit(0); + } + if(strcmp("debug", argv[i]) == 0) { debug = 1; // debug_flags = 0xffffffff; @@ -2766,12 +3272,12 @@ static void parse_args(int argc, char **argv) "\n" " netdata apps.plugin %s\n" " Copyright (C) 2016-2017 Costa Tsaousis <costa@tsaousis.gr>\n" - " Released under GNU Public License v3 or later.\n" + " Released under GNU General Public License v3 or later.\n" " All rights reserved.\n" "\n" " This program is a data collector plugin for netdata.\n" "\n" - " Valid command line options:\n" + " Available command line options:\n" "\n" " SECONDS set the data collection frequency\n" "\n" @@ -2794,7 +3300,7 @@ static void parse_args(int argc, char **argv) " apps_groups.conf\n" " (default NAME=groups)\n" "\n" - " version print program version and exit\n" + " version or -v or -V print program version and exit\n" "\n" , VERSION ); @@ -2819,15 +3325,74 @@ static void parse_args(int argc, char **argv) } } -int main(int argc, char **argv) -{ +static int am_i_running_as_root() { + uid_t uid = getuid(), euid = geteuid(); + + if(uid == 0 || euid == 0) { + if(debug) info("I am running with escalated privileges, uid = %u, euid = %u.", uid, euid); + return 1; + } + + if(debug) info("I am not running with escalated privileges, uid = %u, euid = %u.", uid, euid); + return 0; +} + +#ifdef HAVE_CAPABILITY +static int check_capabilities() { + cap_t caps = cap_get_proc(); + if(!caps) { + error("Cannot get current capabilities."); + return 0; + } + else if(debug) + info("Received my capabilities from the system."); + + int ret = 1; + + cap_flag_value_t cfv = CAP_CLEAR; + if(cap_get_flag(caps, CAP_DAC_READ_SEARCH, CAP_EFFECTIVE, &cfv) == -1) { + error("Cannot find if CAP_DAC_READ_SEARCH is effective."); + ret = 0; + } + else { + if(cfv != CAP_SET) { + error("apps.plugin should run with CAP_DAC_READ_SEARCH."); + ret = 0; + } + else if(debug) + info("apps.plugin runs with CAP_DAC_READ_SEARCH."); + } + + cfv = CAP_CLEAR; + if(cap_get_flag(caps, CAP_SYS_PTRACE, CAP_EFFECTIVE, &cfv) == -1) { + error("Cannot find if CAP_SYS_PTRACE is effective."); + ret = 0; + } + else { + if(cfv != CAP_SET) { + error("apps.plugin should run with CAP_SYS_PTRACE."); + ret = 0; + } + else if(debug) + info("apps.plugin runs with CAP_SYS_PTRACE."); + } + + cap_free(caps); + + return ret; +} +#else +static int check_capabilities() { + return 0; +} +#endif + +int main(int argc, char **argv) { // debug_flags = D_PROCFILE; // set the name for logging program_name = "apps.plugin"; - info("started on pid %d", getpid()); - // disable syslog for apps.plugin error_log_syslog = 0; @@ -2835,12 +3400,12 @@ int main(int argc, char **argv) error_log_errors_per_period = 100; error_log_throttle_period = 3600; - global_host_prefix = getenv("NETDATA_HOST_PREFIX"); - if(global_host_prefix == NULL) { + netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX"); + if(netdata_configured_host_prefix == NULL) { // info("NETDATA_HOST_PREFIX is not passed from netdata"); - global_host_prefix = ""; + netdata_configured_host_prefix = ""; } - // else info("Found NETDATA_HOST_PREFIX='%s'", global_host_prefix); + // else info("Found NETDATA_HOST_PREFIX='%s'", netdata_configured_host_prefix); config_dir = getenv("NETDATA_CONFIG_DIR"); if(config_dir == NULL) { @@ -2854,58 +3419,53 @@ int main(int argc, char **argv) struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; if(setrlimit(RLIMIT_CORE, &rl) != 0) info("Cannot request unlimited core dumps for debugging... Proceeding anyway..."); +#ifdef HAVE_SYS_PRCTL_H prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); +#endif } #endif /* NETDATA_INTERNAL_CHECKS */ procfile_adaptive_initial_allocation = 1; - time_t started_t = now_realtime_sec(); + time_t started_t = now_monotonic_sec(); get_system_HZ(); get_system_pid_max(); 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); - - 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" - "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(!check_capabilities() && !am_i_running_as_root() && !check_proc_1_io()) { + uid_t uid = getuid(), euid = geteuid(); +#ifdef HAVE_CAPABILITY + error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. " + "Without these, apps.plugin cannot report disk I/O utilization of other processes. " + "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; " + "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; " + , uid, euid, argv[0], argv[0], argv[0] ); +#else + error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. " + "Without these, apps.plugin cannot report disk I/O utilization of other processes. " + "Your system does not support capabilities. " + "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; " + , uid, euid, argv[0], argv[0] + ); +#endif + } - if(include_exited_childs) - fprintf(stdout, - "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 - ); + info("started on pid %d", getpid()); + +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) + all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max); +#endif + + all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max); usec_t step = update_every * USEC_PER_SEC; global_iterations_counter = 1; + heartbeat_t hb; + heartbeat_init(&hb); for(;1; global_iterations_counter++) { - usec_t now = now_realtime_usec(); - usec_t next = now - (now % step) + step; #ifdef NETDATA_PROFILING #warning "compiling for profiling" @@ -2913,20 +3473,17 @@ int main(int argc, char **argv) profiling_count++; if(unlikely(profiling_count > 1000)) exit(0); #else - while(now < next) { - sleep_usec(next - now); - now = now_realtime_usec(); - } + heartbeat_next(&hb, step); #endif - if(!collect_data_for_all_processes_from_proc()) { + if(!collect_data_for_all_processes()) { 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); + normalize_utilization(apps_groups_root_target); usec_t dt = send_resource_usage_to_netdata(); @@ -2952,11 +3509,9 @@ int main(int argc, char **argv) show_guest_time_old = show_guest_time; if(unlikely(debug)) - fprintf(stderr, "apps.plugin: done Loop No %llu\n", global_iterations_counter); - - time_t current_t = now_realtime_sec(); + fprintf(stderr, "apps.plugin: done Loop No %zu\n", global_iterations_counter); // restart check (14400 seconds) - if(current_t - started_t > 14400) exit(0); + if(now_monotonic_sec() - started_t > 14400) exit(0); } } @@ -315,9 +315,9 @@ int avl_traverse(avl_tree *t, int (*callback)(void *entry, void *data), void *da void avl_read_lock(avl_tree_lock *t) { #ifndef AVL_WITHOUT_PTHREADS #ifdef AVL_LOCK_WITH_MUTEX - pthread_mutex_lock(&t->mutex); + netdata_mutex_lock(&t->mutex); #else - pthread_rwlock_rdlock(&t->rwlock); + netdata_rwlock_rdlock(&t->rwlock); #endif #endif /* AVL_WITHOUT_PTHREADS */ } @@ -325,9 +325,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); + netdata_mutex_lock(&t->mutex); #else - pthread_rwlock_wrlock(&t->rwlock); + netdata_rwlock_wrlock(&t->rwlock); #endif #endif /* AVL_WITHOUT_PTHREADS */ } @@ -335,9 +335,9 @@ 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); + netdata_mutex_unlock(&t->mutex); #else - pthread_rwlock_unlock(&t->rwlock); + netdata_rwlock_unlock(&t->rwlock); #endif #endif /* AVL_WITHOUT_PTHREADS */ } @@ -352,9 +352,9 @@ void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b)) { int lock; #ifdef AVL_LOCK_WITH_MUTEX - lock = pthread_mutex_init(&t->mutex, NULL); + lock = netdata_mutex_init(&t->mutex, NULL); #else - lock = pthread_rwlock_init(&t->rwlock, NULL); + lock = netdata_rwlock_init(&t->rwlock); #endif if(lock != 0) @@ -13,9 +13,9 @@ // #define AVL_LOCK_WITH_MUTEX 1 #ifdef AVL_LOCK_WITH_MUTEX -#define AVL_LOCK_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#define AVL_LOCK_INITIALIZER NETDATA_MUTEX_INITIALIZER #else /* AVL_LOCK_WITH_MUTEX */ -#define AVL_LOCK_INITIALIZER PTHREAD_RWLOCK_INITIALIZER +#define AVL_LOCK_INITIALIZER NETDATA_RWLOCK_INITIALIZER #endif /* AVL_LOCK_WITH_MUTEX */ #else /* AVL_WITHOUT_PTHREADS */ @@ -41,9 +41,9 @@ typedef struct avl_tree_lock { #ifndef AVL_WITHOUT_PTHREADS #ifdef AVL_LOCK_WITH_MUTEX - pthread_mutex_t mutex; + netdata_mutex_t mutex; #else /* AVL_LOCK_WITH_MUTEX */ - pthread_rwlock_t rwlock; + netdata_rwlock_t rwlock; #endif /* AVL_LOCK_WITH_MUTEX */ #endif /* AVL_WITHOUT_PTHREADS */ } avl_tree_lock; diff --git a/src/backends.c b/src/backends.c index 1272d0473..3e385cab5 100644 --- a/src/backends.c +++ b/src/backends.c @@ -1,16 +1,51 @@ #include "common.h" +// ---------------------------------------------------------------------------- +// How backends work in netdata: +// +// 1. There is an independent thread that runs at the required interval +// (for example, once every 10 seconds) +// +// 2. Every time it wakes, it calls the backend formatting functions to build +// a buffer of data. This is a very fast, memory only operation. +// +// 3. If the buffer already includes data, the new data are appended. +// If the buffer becomes too big, because the data cannot be sent, a +// log is written and the buffer is discarded. +// +// 4. Then it tries to send all the data. It blocks until all the data are sent +// or the socket returns an error. +// If the time required for this is above the interval, it starts skipping +// intervals, but the calculated values include the entire database, without +// gaps (it remembers the timestamps and continues from where it stopped). +// +// 5. repeats the above forever. +// + #define BACKEND_SOURCE_DATA_AS_COLLECTED 0x00000001 #define BACKEND_SOURCE_DATA_AVERAGE 0x00000002 #define BACKEND_SOURCE_DATA_SUM 0x00000004 -static inline calculated_number backend_calculate_value_from_stored_data(RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { + +// ---------------------------------------------------------------------------- +// helper functions for backends + +// calculate the SUM or AVERAGE of a dimension, for any timeframe +// may return NAN if the database does not have any value in the give timeframe + +static inline calculated_number backend_calculate_value_from_stored_data( + RRDSET *st // the chart + , RRDDIM *rd // the dimension + , time_t after // the start timestamp + , time_t before // the end timestamp + , uint32_t options // BACKEND_SOURCE_* bitmap +) { + // find the edges of the rrd database for this chart time_t first_t = rrdset_first_entry_t(st); - time_t last_t = rrdset_last_entry_t(st); + time_t last_t = rrdset_last_entry_t(st); - if(unlikely(before - after < st->update_every && after != after - after % st->update_every)) - // when st->update_every is bigger than the frequency we send data to backend - // skip the iterations that are not aligned to the database + if(unlikely(before < first_t || after > last_t)) + // the chart has not been updated in the wanted timeframe return NAN; // align the time-frame @@ -22,7 +57,7 @@ static inline calculated_number backend_calculate_value_from_stored_data(RRDSET after = first_t; if(unlikely(after > before)) - // this can happen when the st->update_every > before - after + // this can happen when st->update_every > before - after before = after; if(unlikely(before > last_t)) @@ -36,14 +71,20 @@ static inline calculated_number backend_calculate_value_from_stored_data(RRDSET slot, stop_now = 0; for(slot = start_at_slot; !stop_now ; slot--) { + if(unlikely(slot < 0)) slot = st->entries - 1; if(unlikely(slot == stop_at_slot)) stop_now = 1; storage_number n = rd->values[slot]; - if(unlikely(!does_storage_number_exist(n))) continue; + + if(unlikely(!does_storage_number_exist(n))) { + // not collected + continue; + } calculated_number value = unpack_storage_number(n); sum += value; + counter++; } @@ -56,84 +97,295 @@ static inline calculated_number backend_calculate_value_from_stored_data(RRDSET return sum / (calculated_number)counter; } -static inline int format_dimension_collected_graphite_plaintext(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { + +// discard a response received by a backend +// after logging a simple of it to error.log + +static inline int discard_response(BUFFER *b, const char *backend) { + char sample[1024]; + const char *s = buffer_tostring(b); + char *d = sample, *e = &sample[sizeof(sample) - 1]; + + for(; *s && d < e ;s++) { + char c = *s; + if(unlikely(!isprint(c))) c = ' '; + *d++ = c; + } + *d = '\0'; + + info("Received %zu bytes from %s backend. Ignoring them. Sample: '%s'", buffer_strlen(b), backend, sample); + buffer_flush(b); + return 0; +} + + +// ---------------------------------------------------------------------------- +// graphite backend + +static inline int format_dimension_collected_graphite_plaintext( + BUFFER *b // the buffer to write data to + , const char *prefix // the prefix to use + , RRDHOST *host // the host this chart comes from + , const char *hostname // the hostname (to override host->hostname) + , RRDSET *st // the chart + , RRDDIM *rd // the dimension + , time_t after // the start timestamp + , time_t before // the end timestamp + , uint32_t options // BACKEND_SOURCE_* bitmap +) { (void)host; (void)after; (void)before; (void)options; - buffer_sprintf(b, "%s.%s.%s.%s " COLLECTED_NUMBER_FORMAT " %u\n", prefix, hostname, st->id, rd->id, rd->last_collected_value, (uint32_t)rd->last_collected_time.tv_sec); + + buffer_sprintf( + b + , "%s.%s.%s.%s " COLLECTED_NUMBER_FORMAT " %u\n" + , prefix + , hostname + , st->id + , rd->id + , rd->last_collected_value + , (uint32_t)rd->last_collected_time.tv_sec + ); + return 1; } -static inline int format_dimension_stored_graphite_plaintext(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { +static inline int format_dimension_stored_graphite_plaintext( + BUFFER *b // the buffer to write data to + , const char *prefix // the prefix to use + , RRDHOST *host // the host this chart comes from + , const char *hostname // the hostname (to override host->hostname) + , RRDSET *st // the chart + , RRDDIM *rd // the dimension + , time_t after // the start timestamp + , time_t before // the end timestamp + , uint32_t options // BACKEND_SOURCE_* bitmap +) { (void)host; + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options); + if(!isnan(value)) { - buffer_sprintf(b, "%s.%s.%s.%s " CALCULATED_NUMBER_FORMAT " %u\n", prefix, hostname, st->id, rd->id, value, (uint32_t) before); + + buffer_sprintf( + b + , "%s.%s.%s.%s " CALCULATED_NUMBER_FORMAT " %u\n" + , prefix + , hostname + , st->id + , rd->id + , value + , (uint32_t) before + ); + return 1; } return 0; } -static inline int format_dimension_collected_opentsdb_telnet(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { +static inline int process_graphite_response(BUFFER *b) { + return discard_response(b, "graphite"); +} + + +// ---------------------------------------------------------------------------- +// opentsdb backend + +static inline int format_dimension_collected_opentsdb_telnet( + BUFFER *b // the buffer to write data to + , const char *prefix // the prefix to use + , RRDHOST *host // the host this chart comes from + , const char *hostname // the hostname (to override host->hostname) + , RRDSET *st // the chart + , RRDDIM *rd // the dimension + , time_t after // the start timestamp + , time_t before // the end timestamp + , uint32_t options // BACKEND_SOURCE_* bitmap +) { (void)host; (void)after; (void)before; (void)options; - buffer_sprintf(b, "put %s.%s.%s %u " COLLECTED_NUMBER_FORMAT " host=%s\n", prefix, st->id, rd->id, (uint32_t)rd->last_collected_time.tv_sec, rd->last_collected_value, hostname); + + buffer_sprintf( + b + , "put %s.%s.%s %u " COLLECTED_NUMBER_FORMAT " host=%s\n" + , prefix + , st->id + , rd->id + , (uint32_t)rd->last_collected_time.tv_sec + , rd->last_collected_value + , hostname + ); + return 1; } -static inline int format_dimension_stored_opentsdb_telnet(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) { +static inline int format_dimension_stored_opentsdb_telnet( + BUFFER *b // the buffer to write data to + , const char *prefix // the prefix to use + , RRDHOST *host // the host this chart comes from + , const char *hostname // the hostname (to override host->hostname) + , RRDSET *st // the chart + , RRDDIM *rd // the dimension + , time_t after // the start timestamp + , time_t before // the end timestamp + , uint32_t options // BACKEND_SOURCE_* bitmap +) { (void)host; + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options); + if(!isnan(value)) { - buffer_sprintf(b, "put %s.%s.%s %u " CALCULATED_NUMBER_FORMAT " host=%s\n", prefix, st->id, rd->id, (uint32_t) before, value, hostname); + + buffer_sprintf( + b + , "put %s.%s.%s %u " CALCULATED_NUMBER_FORMAT " host=%s\n" + , prefix + , st->id + , rd->id + , (uint32_t) before + , value + , hostname + ); + return 1; } return 0; } -static inline int process_graphite_response(BUFFER *b) { - char sample[1024]; - const char *s = buffer_tostring(b); - char *d = sample, *e = &sample[sizeof(sample) - 1]; +static inline int process_opentsdb_response(BUFFER *b) { + return discard_response(b, "opentsdb"); +} - for(; *s && d < e ;s++) { - char c = *s; - if(unlikely(!isprint(c))) c = ' '; - *d++ = c; - } - *d = '\0'; - info("Received %zu bytes from graphite backend. Ignoring them. Sample: '%s'", buffer_strlen(b), sample); - buffer_flush(b); - return 0; +// ---------------------------------------------------------------------------- +// json backend + +static inline int format_dimension_collected_json_plaintext( + BUFFER *b // the buffer to write data to + , const char *prefix // the prefix to use + , RRDHOST *host // the host this chart comes from + , const char *hostname // the hostname (to override host->hostname) + , RRDSET *st // the chart + , RRDDIM *rd // the dimension + , time_t after // the start timestamp + , time_t before // the end timestamp + , uint32_t options // BACKEND_SOURCE_* bitmap +) { + (void)host; + (void)after; + (void)before; + (void)options; + + buffer_sprintf(b, "{" + "\"prefix\":\"%s\"," + "\"hostname\":\"%s\"," + + "\"chart_id\":\"%s\"," + "\"chart_name\":\"%s\"," + "\"chart_family\":\"%s\"," + "\"chart_context\": \"%s\"," + "\"chart_type\":\"%s\"," + "\"units\": \"%s\"," + + "\"id\":\"%s\"," + "\"name\":\"%s\"," + "\"value\":" COLLECTED_NUMBER_FORMAT "," + + "\"timestamp\": %u}\n", + prefix, + hostname, + + st->id, + st->name, + st->family, + st->context, + st->type, + st->units, + + rd->id, + rd->name, + rd->last_collected_value, + + (uint32_t)rd->last_collected_time.tv_sec + ); + + return 1; } -static inline int process_opentsdb_response(BUFFER *b) { - char sample[1024]; - const char *s = buffer_tostring(b); - char *d = sample, *e = &sample[sizeof(sample) - 1]; +static inline int format_dimension_stored_json_plaintext( + BUFFER *b // the buffer to write data to + , const char *prefix // the prefix to use + , RRDHOST *host // the host this chart comes from + , const char *hostname // the hostname (to override host->hostname) + , RRDSET *st // the chart + , RRDDIM *rd // the dimension + , time_t after // the start timestamp + , time_t before // the end timestamp + , uint32_t options // BACKEND_SOURCE_* bitmap +) { + (void)host; - for(; *s && d < e ;s++) { - char c = *s; - if(unlikely(!isprint(c))) c = ' '; - *d++ = c; - } - *d = '\0'; + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options); - info("Received %zu bytes from opentsdb backend. Ignoring them. Sample: '%s'", buffer_strlen(b), sample); - buffer_flush(b); + if(!isnan(value)) { + buffer_sprintf(b, "{" + "\"prefix\":\"%s\"," + "\"hostname\":\"%s\"," + + "\"chart_id\":\"%s\"," + "\"chart_name\":\"%s\"," + "\"chart_family\":\"%s\"," + "\"chart_context\": \"%s\"," + "\"chart_type\":\"%s\"," + "\"units\": \"%s\"," + + "\"id\":\"%s\"," + "\"name\":\"%s\"," + "\"value\":" CALCULATED_NUMBER_FORMAT "," + + "\"timestamp\": %u}\n", + prefix, + hostname, + + st->id, + st->name, + st->family, + st->context, + st->type, + st->units, + + rd->id, + rd->name, + value, + + (uint32_t)before + ); + + return 1; + } return 0; } +static inline int process_json_response(BUFFER *b) { + return discard_response(b, "json"); +} + + +// ---------------------------------------------------------------------------- +// the backend thread + void *backends_main(void *ptr) { + int default_port = 0; + int sock = -1; struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; BUFFER *b = buffer_create(1), *response = buffer_create(1); - int (*backend_request_formatter)(BUFFER *b, const char *prefix, RRDHOST *host, const char *hostname, RRDSET *st, RRDDIM *rd, time_t after, time_t before, uint32_t options) = NULL; - int (*backend_response_checker)(BUFFER *b) = NULL; + int (*backend_request_formatter)(BUFFER *, const char *, RRDHOST *, const char *, RRDSET *, RRDDIM *, time_t, time_t, uint32_t) = NULL; + int (*backend_response_checker)(BUFFER *) = NULL; info("BACKEND thread created with task id %d", gettid()); @@ -150,22 +402,21 @@ void *backends_main(void *ptr) { .tv_sec = 0, .tv_usec = 0 }; - int default_port = 0; - int sock = -1; uint32_t options; - int enabled = config_get_boolean("backend", "enabled", 0); - const char *source = config_get("backend", "data source", "average"); - const char *type = config_get("backend", "type", "graphite"); - const char *destination = config_get("backend", "destination", "localhost"); - const char *prefix = config_get("backend", "prefix", "netdata"); - const char *hostname = config_get("backend", "hostname", localhost.hostname); - int frequency = (int)config_get_number("backend", "update every", 10); - int buffer_on_failures = (int)config_get_number("backend", "buffer on failures", 10); - long timeoutms = config_get_number("backend", "timeout ms", frequency * 2 * 1000); + int enabled = config_get_boolean(CONFIG_SECTION_BACKEND, "enabled", 0); + const char *source = config_get(CONFIG_SECTION_BACKEND, "data source", "average"); + const char *type = config_get(CONFIG_SECTION_BACKEND, "type", "graphite"); + const char *destination = config_get(CONFIG_SECTION_BACKEND, "destination", "localhost"); + const char *prefix = config_get(CONFIG_SECTION_BACKEND, "prefix", "netdata"); + const char *hostname = config_get(CONFIG_SECTION_BACKEND, "hostname", localhost->hostname); + int frequency = (int)config_get_number(CONFIG_SECTION_BACKEND, "update every", 10); + int buffer_on_failures = (int)config_get_number(CONFIG_SECTION_BACKEND, "buffer on failures", 10); + long timeoutms = config_get_number(CONFIG_SECTION_BACKEND, "timeout ms", frequency * 2 * 1000); // ------------------------------------------------------------------------ // validate configuration options // and prepare for sending data to our backend + if(!enabled || frequency < 1) goto cleanup; @@ -183,23 +434,49 @@ void *backends_main(void *ptr) { goto cleanup; } + if(timeoutms < 1) { + error("BACKED invalid timeout %ld ms given. Assuming %d ms.", timeoutms, frequency * 2 * 1000); + timeoutms = frequency * 2 * 1000; + } + timeout.tv_sec = (timeoutms * 1000) / 1000000; + timeout.tv_usec = (timeoutms * 1000) % 1000000; + + + // ------------------------------------------------------------------------ + // select the backend type + if(!strcmp(type, "graphite") || !strcmp(type, "graphite:plaintext")) { + default_port = 2003; + backend_response_checker = process_graphite_response; + if(options == BACKEND_SOURCE_DATA_AS_COLLECTED) backend_request_formatter = format_dimension_collected_graphite_plaintext; else backend_request_formatter = format_dimension_stored_graphite_plaintext; - backend_response_checker = process_graphite_response; } else if(!strcmp(type, "opentsdb") || !strcmp(type, "opentsdb:telnet")) { + default_port = 4242; + backend_response_checker = process_opentsdb_response; + if(options == BACKEND_SOURCE_DATA_AS_COLLECTED) backend_request_formatter = format_dimension_collected_opentsdb_telnet; else backend_request_formatter = format_dimension_stored_opentsdb_telnet; - backend_response_checker = process_opentsdb_response; + } + else if (!strcmp(type, "json") || !strcmp(type, "json:plaintext")) { + + default_port = 5448; + backend_response_checker = process_json_response; + + if (options == BACKEND_SOURCE_DATA_AS_COLLECTED) + backend_request_formatter = format_dimension_collected_json_plaintext; + else + backend_request_formatter = format_dimension_stored_json_plaintext; + } else { error("Unknown backend type '%s'", type); @@ -211,15 +488,9 @@ void *backends_main(void *ptr) { goto cleanup; } - if(timeoutms < 1) { - error("BACKED invalid timeout %ld ms given. Assuming %d ms.", timeoutms, frequency * 2 * 1000); - timeoutms = frequency * 2 * 1000; - } - timeout.tv_sec = (timeoutms * 1000) / 1000000; - timeout.tv_usec = (timeoutms * 1000) % 1000000; // ------------------------------------------------------------------------ - // prepare the charts for monitoring the backend + // prepare the charts for monitoring the backend operation struct rusage thread; @@ -238,32 +509,23 @@ void *backends_main(void *ptr) { chart_backend_reconnects = 0, chart_backend_latency = 0; - RRDSET *chart_metrics = rrdset_find("netdata.backend_metrics"); - if(!chart_metrics) { - chart_metrics = rrdset_create("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", 130600, frequency, RRDSET_TYPE_LINE); - rrddim_add(chart_metrics, "buffered", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(chart_metrics, "lost", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(chart_metrics, "sent", NULL, 1, 1, RRDDIM_ABSOLUTE); - } + RRDSET *chart_metrics = rrdset_create_localhost("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", 130600, frequency, RRDSET_TYPE_LINE); + rrddim_add(chart_metrics, "buffered", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(chart_metrics, "lost", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(chart_metrics, "sent", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); - RRDSET *chart_bytes = rrdset_find("netdata.backend_bytes"); - if(!chart_bytes) { - chart_bytes = rrdset_create("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", 130610, frequency, RRDSET_TYPE_AREA); - rrddim_add(chart_bytes, "buffered", NULL, 1, 1024, RRDDIM_ABSOLUTE); - rrddim_add(chart_bytes, "lost", NULL, 1, 1024, RRDDIM_ABSOLUTE); - rrddim_add(chart_bytes, "sent", NULL, 1, 1024, RRDDIM_ABSOLUTE); - rrddim_add(chart_bytes, "received", NULL, 1, 1024, RRDDIM_ABSOLUTE); - } + RRDSET *chart_bytes = rrdset_create_localhost("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", 130610, frequency, RRDSET_TYPE_AREA); + rrddim_add(chart_bytes, "buffered", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(chart_bytes, "lost", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(chart_bytes, "sent", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(chart_bytes, "received", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); - RRDSET *chart_ops = rrdset_find("netdata.backend_ops"); - if(!chart_ops) { - chart_ops = rrdset_create("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", 130630, frequency, RRDSET_TYPE_LINE); - rrddim_add(chart_ops, "write", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(chart_ops, "discard", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(chart_ops, "reconnect", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(chart_ops, "failure", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(chart_ops, "read", NULL, 1, 1, RRDDIM_ABSOLUTE); - } + RRDSET *chart_ops = rrdset_create_localhost("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", 130630, frequency, RRDSET_TYPE_LINE); + rrddim_add(chart_ops, "write", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(chart_ops, "discard", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(chart_ops, "reconnect", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(chart_ops, "failure", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(chart_ops, "read", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); /* * this is misleading - we can only measure the time we need to send data @@ -272,19 +534,14 @@ void *backends_main(void *ptr) { * * issue #1432 and https://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html * - RRDSET *chart_latency = rrdset_find("netdata.backend_latency"); - if(!chart_latency) { - chart_latency = rrdset_create("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", 130620, frequency, RRDSET_TYPE_AREA); - rrddim_add(chart_latency, "latency", NULL, 1, 1000, RRDDIM_ABSOLUTE); - } + RRDSET *chart_latency = rrdset_create_localhost("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", 130620, frequency, RRDSET_TYPE_AREA); + rrddim_add(chart_latency, "latency", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); */ - RRDSET *chart_rusage = rrdset_find("netdata.backend_thread_cpu"); - if(!chart_rusage) { - chart_rusage = rrdset_create("netdata", "backend_thread_cpu", NULL, "backend", NULL, "NetData Backend Thread CPU usage", "milliseconds/s", 130630, frequency, RRDSET_TYPE_STACKED); - rrddim_add(chart_rusage, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); - rrddim_add(chart_rusage, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); - } + RRDSET *chart_rusage = rrdset_create_localhost("netdata", "backend_thread_cpu", NULL, "backend", NULL, "NetData Backend Thread CPU usage", "milliseconds/s", 130630, frequency, RRDSET_TYPE_STACKED); + rrddim_add(chart_rusage, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(chart_rusage, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + // ------------------------------------------------------------------------ // prepare the backend main loop @@ -292,49 +549,49 @@ void *backends_main(void *ptr) { info("BACKEND configured ('%s' on '%s' sending '%s' data, every %d seconds, as host '%s', with prefix '%s')", type, destination, source, frequency, hostname, prefix); usec_t step_ut = frequency * USEC_PER_SEC; - usec_t random_ut = now_realtime_usec() % (step_ut / 2); - time_t before = (time_t)((now_realtime_usec() - step_ut) / USEC_PER_SEC); - time_t after = before; + time_t after = now_realtime_sec(); int failures = 0; + heartbeat_t hb; + heartbeat_init(&hb); for(;;) { - // ------------------------------------------------------------------------ - // wait for the next iteration point - usec_t now_ut = now_realtime_usec(); - usec_t next_ut = now_ut - (now_ut % step_ut) + step_ut; - before = (time_t)(next_ut / USEC_PER_SEC); - - // add a little delay (1/4 of the step) plus some randomness - next_ut += (step_ut / 4) + random_ut; + // ------------------------------------------------------------------------ + // Wait for the next iteration point. + heartbeat_next(&hb, step_ut); + time_t before = now_realtime_sec(); - while(now_ut < next_ut) { - sleep_usec(next_ut - now_ut); - now_ut = now_realtime_usec(); - } // ------------------------------------------------------------------------ // add to the buffer the data we need to send to the backend - RRDSET *st; int pthreadoldcancelstate; if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 0)) error("Cannot set pthread cancel state to DISABLE."); - rrdhost_rdlock(&localhost); - for(st = localhost.rrdset_root; st ;st = st->next) { - pthread_rwlock_rdlock(&st->rwlock); + rrd_rdlock(); + RRDHOST *host; + rrdhost_foreach_read(host) { + if(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE) + continue; - RRDDIM *rd; - for(rd = st->dimensions; rd ;rd = rd->next) { - if(rd->last_collected_time.tv_sec >= after) - chart_buffered_metrics += backend_request_formatter(b, prefix, &localhost, hostname, st, rd, after, before, options); - } + rrdhost_rdlock(host); - pthread_rwlock_unlock(&st->rwlock); + RRDSET *st; + rrdset_foreach_read(st, host) { + rrdset_rdlock(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if(rd->last_collected_time.tv_sec >= after) + chart_buffered_metrics += backend_request_formatter(b, prefix, host, (host == localhost)?hostname:host->hostname, st, rd, after, before, options); + } + rrdset_unlock(st); + } + rrdhost_unlock(host); } - rrdhost_unlock(&localhost); + rrd_unlock(); if(unlikely(pthread_setcancelstate(pthreadoldcancelstate, NULL) != 0)) error("Cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate); @@ -360,6 +617,10 @@ void *backends_main(void *ptr) { //fprintf(stderr, "\nBACKEND BEGIN:\n%s\nBACKEND END\n", buffer_tostring(b)); // FIXME //fprintf(stderr, "after = %lu, before = %lu\n", after, before); + // prepare for the next iteration + // to add incrementally data to buffer + after = before; + // ------------------------------------------------------------------------ // if we are connected, receive a response, without blocking @@ -399,28 +660,13 @@ void *backends_main(void *ptr) { // if we are not connected, connect to a backend server if(unlikely(sock == -1)) { - usec_t start_ut = now_realtime_usec(); - const char *s = destination; - while(*s) { - const 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); - chart_backend_reconnects++; - sock = connect_to(buf, default_port, &timeout); - if(sock != -1) break; - s = e; - } - chart_backend_latency += now_realtime_usec() - start_ut; + usec_t start_ut = now_monotonic_usec(); + size_t reconnects = 0; + + sock = connect_to_one_of(destination, default_port, &timeout, &reconnects, NULL, 0); + + chart_backend_reconnects += reconnects; + chart_backend_latency += now_monotonic_usec() - start_ut; } if(unlikely(netdata_exit)) break; @@ -430,14 +676,14 @@ void *backends_main(void *ptr) { if(likely(sock != -1)) { size_t len = buffer_strlen(b); - usec_t start_ut = now_realtime_usec(); + usec_t start_ut = now_monotonic_usec(); int flags = 0; #ifdef MSG_NOSIGNAL flags += MSG_NOSIGNAL; #endif ssize_t written = send(sock, buffer_tostring(b), len, flags); - chart_backend_latency += now_realtime_usec() - start_ut; + chart_backend_latency += now_monotonic_usec() - start_ut; if(written != -1 && (size_t)written == len) { // we sent the data successfully chart_transmission_successes++; @@ -465,12 +711,6 @@ void *backends_main(void *ptr) { close(sock); sock = -1; } - - // either the buffer is empty - // or is holding the data we couldn't send - // so, make sure the next iteration will continue - // from where we are now - after = before; } else { error("Failed to update database backend '%s'", destination); @@ -495,7 +735,7 @@ void *backends_main(void *ptr) { // ------------------------------------------------------------------------ // update the monitoring charts - if(chart_ops->counter_done) rrdset_next(chart_ops); + if(likely(chart_ops->counter_done)) rrdset_next(chart_ops); rrddim_set(chart_ops, "read", chart_receptions); rrddim_set(chart_ops, "write", chart_transmission_successes); rrddim_set(chart_ops, "discard", chart_data_lost_events); @@ -503,13 +743,13 @@ void *backends_main(void *ptr) { rrddim_set(chart_ops, "reconnect", chart_backend_reconnects); rrdset_done(chart_ops); - if(chart_metrics->counter_done) rrdset_next(chart_metrics); + if(likely(chart_metrics->counter_done)) rrdset_next(chart_metrics); rrddim_set(chart_metrics, "buffered", chart_buffered_metrics); rrddim_set(chart_metrics, "lost", chart_lost_metrics); rrddim_set(chart_metrics, "sent", chart_sent_metrics); rrdset_done(chart_metrics); - if(chart_bytes->counter_done) rrdset_next(chart_bytes); + if(likely(chart_bytes->counter_done)) rrdset_next(chart_bytes); rrddim_set(chart_bytes, "buffered", chart_buffered_bytes); rrddim_set(chart_bytes, "lost", chart_lost_bytes); rrddim_set(chart_bytes, "sent", chart_sent_bytes); @@ -517,13 +757,13 @@ void *backends_main(void *ptr) { rrdset_done(chart_bytes); /* - if(chart_latency->counter_done) rrdset_next(chart_latency); + if(likely(chart_latency->counter_done)) rrdset_next(chart_latency); rrddim_set(chart_latency, "latency", chart_backend_latency); rrdset_done(chart_latency); */ getrusage(RUSAGE_THREAD, &thread); - if(chart_rusage->counter_done) rrdset_next(chart_rusage); + if(likely(chart_rusage->counter_done)) rrdset_next(chart_rusage); rrddim_set(chart_rusage, "user", thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec); rrddim_set(chart_rusage, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec); rrdset_done(chart_rusage); diff --git a/src/clocks.c b/src/clocks.c index c90a07c2f..879ebf911 100644 --- a/src/clocks.c +++ b/src/clocks.c @@ -6,64 +6,89 @@ inline int clock_gettime(clockid_t clk_id, struct timespec *ts) { if(unlikely(gettimeofday(&tv, NULL) == -1)) return -1; ts->tv_sec = tv.tv_sec; - ts->tv_nsec = tv.tv_usec * NSEC_PER_USEC; + ts->tv_nsec = (tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC; return 0; } #endif -inline time_t now_realtime_sec(void) { +static inline time_t now_sec(clockid_t clk_id) { struct timespec ts; - if(unlikely(clock_gettime(CLOCK_REALTIME, &ts) == -1)) + if(unlikely(clock_gettime(clk_id, &ts) == -1)) return 0; return ts.tv_sec; } -inline int now_realtime_timeval(struct timeval *tv) { +static inline usec_t now_usec(clockid_t clk_id) { + struct timespec ts; + if(unlikely(clock_gettime(clk_id, &ts) == -1)) + return 0; + return (usec_t)ts.tv_sec * USEC_PER_SEC + (ts.tv_nsec % NSEC_PER_SEC) / NSEC_PER_USEC; +} + +static inline int now_timeval(clockid_t clk_id, struct timeval *tv) { struct timespec ts; - if(unlikely(clock_gettime(CLOCK_REALTIME, &ts) == -1)) + + if(unlikely(clock_gettime(clk_id, &ts) == -1)) { + tv->tv_sec = 0; + tv->tv_usec = 0; return -1; + } + tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; + tv->tv_usec = (suseconds_t)((ts.tv_nsec % NSEC_PER_SEC) / NSEC_PER_USEC); return 0; } +inline time_t now_realtime_sec(void) { + return now_sec(CLOCK_REALTIME); +} + inline usec_t now_realtime_usec(void) { - struct timespec ts; - if(unlikely(clock_gettime(CLOCK_REALTIME, &ts) == -1)) - return 0; - return (usec_t)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; + return now_usec(CLOCK_REALTIME); +} + +inline int now_realtime_timeval(struct timeval *tv) { + return now_timeval(CLOCK_REALTIME, tv); } inline time_t now_monotonic_sec(void) { - struct timespec ts; - if(unlikely(clock_gettime(CLOCK_MONOTONIC, &ts) == -1)) - return 0; - return ts.tv_sec; + return now_sec(CLOCK_MONOTONIC); } inline usec_t now_monotonic_usec(void) { - struct timespec ts; - if(unlikely(clock_gettime(CLOCK_MONOTONIC, &ts) == -1)) - return 0; - return (usec_t)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; + return now_usec(CLOCK_MONOTONIC); +} + +inline int now_monotonic_timeval(struct timeval *tv) { + return now_timeval(CLOCK_MONOTONIC, tv); } inline time_t now_boottime_sec(void) { - struct timespec ts; - if(unlikely(clock_gettime(CLOCK_BOOTTIME, &ts) == -1)) - return 0; - return ts.tv_sec; + return now_sec(CLOCK_BOOTTIME); } inline usec_t now_boottime_usec(void) { - struct timespec ts; - if(unlikely(clock_gettime(CLOCK_BOOTTIME, &ts) == -1)) - return 0; - return (usec_t)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; + return now_usec(CLOCK_BOOTTIME); +} + +inline int now_boottime_timeval(struct timeval *tv) { + return now_timeval(CLOCK_BOOTTIME, tv); } inline usec_t timeval_usec(struct timeval *tv) { - return (usec_t)tv->tv_sec * USEC_PER_SEC + tv->tv_usec; + return (usec_t)tv->tv_sec * USEC_PER_SEC + (tv->tv_usec % USEC_PER_SEC); +} + +inline msec_t timeval_msec(struct timeval *tv) { + return (msec_t)tv->tv_sec * MSEC_PER_SEC + ((tv->tv_usec % USEC_PER_SEC) / MSEC_PER_SEC); +} + +inline susec_t dt_usec_signed(struct timeval *now, struct timeval *old) { + usec_t ts1 = timeval_usec(now); + usec_t ts2 = timeval_usec(old); + + if(likely(ts1 >= ts2)) return (susec_t)(ts1 - ts2); + return -((susec_t)(ts2 - ts1)); } inline usec_t dt_usec(struct timeval *now, struct timeval *old) { @@ -71,3 +96,38 @@ inline usec_t dt_usec(struct timeval *now, struct timeval *old) { usec_t ts2 = timeval_usec(old); return (ts1 > ts2) ? (ts1 - ts2) : (ts2 - ts1); } + +inline void heartbeat_init(heartbeat_t *hb) +{ + *hb = 0ULL; +} + +usec_t heartbeat_next(heartbeat_t *hb, usec_t tick) +{ + heartbeat_t now = now_monotonic_usec(); + usec_t next = now - (now % tick) + tick; + + while(now < next) { + sleep_usec(next - now); + now = now_monotonic_usec(); + } + + if(likely(*hb != 0ULL)) { + usec_t dt = now - *hb; + *hb = now; + if(unlikely(dt / tick > 1)) + error("heartbeat missed between %llu usec and %llu usec", *hb, now); + return dt; + } + else { + *hb = now; + return 0ULL; + } +} + +inline usec_t heartbeat_dt_usec(heartbeat_t *hb) +{ + if(!*hb) + return 0ULL; + return now_monotonic_usec() - *hb; +} diff --git a/src/clocks.h b/src/clocks.h index c1b8e7017..197b5431f 100644 --- a/src/clocks.h +++ b/src/clocks.h @@ -12,9 +12,12 @@ struct timespec { typedef int clockid_t; #endif -#ifndef HAVE_CLOCK_GETTIME -int clock_gettime(clockid_t clk_id, struct timespec *ts); -#endif +typedef unsigned long long nsec_t; +typedef unsigned long long msec_t; +typedef unsigned long long usec_t; +typedef long long susec_t; + +typedef usec_t heartbeat_t; /* Linux value is as good as any other */ #ifndef CLOCK_REALTIME @@ -27,20 +30,30 @@ int clock_gettime(clockid_t clk_id, struct timespec *ts); #endif #ifndef CLOCK_BOOTTIME -/* fallback to CLOCK_MONOTONIC if not available */ + +#ifdef CLOCK_UPTIME +/* CLOCK_BOOTTIME falls back to CLOCK_UPTIME on FreeBSD */ +#define CLOCK_BOOTTIME CLOCK_UPTIME +#else // CLOCK_UPTIME +/* CLOCK_BOOTTIME falls back to CLOCK_MONOTONIC */ #define CLOCK_BOOTTIME CLOCK_MONOTONIC -#else +#endif // CLOCK_UPTIME + +#else // CLOCK_BOOTTIME + #ifdef HAVE_CLOCK_GETTIME #define CLOCK_BOOTTIME_IS_AVAILABLE 1 // required for /proc/uptime -#endif -#endif +#endif // HAVE_CLOCK_GETTIME -typedef unsigned long long usec_t; +#endif // CLOCK_BOOTTIME -#define NSEC_PER_SEC 1000000000ULL #define NSEC_PER_MSEC 1000000ULL + +#define NSEC_PER_SEC 1000000000ULL #define NSEC_PER_USEC 1000ULL + #define USEC_PER_SEC 1000000ULL +#define MSEC_PER_SEC 1000ULL #ifndef HAVE_CLOCK_GETTIME /* Fallback function for POSIX.1-2001 clock_gettime() function. @@ -52,41 +65,58 @@ typedef unsigned long long usec_t; extern int clock_gettime(clockid_t clk_id, struct timespec *ts); #endif -/* Fills struct timeval with time since EPOCH from real-time clock (i.e. wall-clock). - * - Hibernation/suspend time is included - * - adjtime()/NTP adjustments affect this clock - * Return 0 on succes, -1 else with errno set appropriately. +/* + * Three clocks are available (cf. man 3 clock_gettime): + * + * REALTIME clock (i.e. wall-clock): + * This clock is affected by discontinuous jumps in the system time + * (e.g., if the system administrator manually changes the clock), and by the incremental adjustments performed by adjtime(3) and NTP. + * + * MONOTONIC clock + * Clock that cannot be set and represents monotonic time since some unspecified starting point. + * This clock is not affected by discontinuous jumps in the system time + * (e.g., if the system administrator manually changes the clock), but is affected by the incremental adjustments performed by adjtime(3) and NTP. + * If not available on the system, this clock falls back to REALTIME clock. + * + * BOOTTIME clock + * Identical to CLOCK_MONOTONIC, except it also includes any time that the system is suspended. + * This allows applications to get a suspend-aware monotonic clock without having to deal with the complications of CLOCK_REALTIME, + * which may have discontinuities if the time is changed using settimeofday(2). + * If not available on the system, this clock falls back to MONOTONIC clock. + * + * All now_*_timeval() functions fill the `struct timeval` with the time from the appropriate clock. + * Those functions return 0 on success, -1 else with errno set appropriately. + * + * All now_*_sec() functions return the time in seconds from the approriate clock, or 0 on error. + * All now_*_usec() functions return the time in microseconds from the approriate clock, or 0 on error. */ extern int now_realtime_timeval(struct timeval *tv); - -/* Returns time since EPOCH from real-time clock (i.e. wall-clock). - * - Hibernation/suspend time is included - * - adjtime()/NTP adjustments affect this clock - */ extern time_t now_realtime_sec(void); extern usec_t now_realtime_usec(void); -/* Returns time from monotonic clock if available, real-time clock else. - * If monotonic clock is available: - * - hibernation/suspend time is not included - * - adjtime()/NTP adjusments affect this clock - * If monotonic clock is not available, this fallbacks to now_realtime(). - */ +extern int now_monotonic_timeval(struct timeval *tv); extern time_t now_monotonic_sec(void); extern usec_t now_monotonic_usec(void); -/* Returns time from boottime clock if available, - * monotonic clock else if available, real-time clock else. - * If boottime clock is available: - * - hibernation/suspend time is included - * - adjtime()/NTP adjusments affect this clock - * If boottime clock is not available, this fallbacks to now_monotonic(). - * If monotonic clock is not available, this fallbacks to now_realtime(). - */ +extern int now_boottime_timeval(struct timeval *tv); extern time_t now_boottime_sec(void); extern usec_t now_boottime_usec(void); + extern usec_t timeval_usec(struct timeval *ts); +extern msec_t timeval_msec(struct timeval *tv); + extern usec_t dt_usec(struct timeval *now, struct timeval *old); +extern susec_t dt_usec_signed(struct timeval *now, struct timeval *old); + +extern void heartbeat_init(heartbeat_t *hb); + +/* Sleeps until next multiple of tick using monotonic clock. + * Returns elapsed time in microseconds since previous heartbeat + */ +extern usec_t heartbeat_next(heartbeat_t *hb, usec_t tick); + +/* Returns elapsed time in microseconds since last heartbeat */ +extern usec_t heartbeat_dt_usec(heartbeat_t *hb); #endif /* NETDATA_CLOCKS_H */ diff --git a/src/common.c b/src/common.c index 42f3d8d15..88fcf85bc 100644 --- a/src/common.c +++ b/src/common.c @@ -8,10 +8,21 @@ # define MADV_DONTFORK INHERIT_NONE #endif /* __FreeBSD__ || __APPLE__*/ -char *global_host_prefix = ""; +char *netdata_configured_hostname = NULL; +char *netdata_configured_config_dir = NULL; +char *netdata_configured_log_dir = NULL; +char *netdata_configured_plugins_dir = NULL; +char *netdata_configured_web_dir = NULL; +char *netdata_configured_cache_dir = NULL; +char *netdata_configured_varlib_dir = NULL; +char *netdata_configured_home_dir = NULL; +char *netdata_configured_host_prefix = NULL; + int enable_ksm = 1; volatile sig_atomic_t netdata_exit = 0; +const char *os_type = NETDATA_OS_TYPE; +const char *program_version = VERSION; // ---------------------------------------------------------------------------- // memory allocation functions that handle failures @@ -903,6 +914,9 @@ char *trim(char *s) { } void *mymmap(const char *filename, size_t size, int flags, int ksm) { +#ifndef MADV_MERGEABLE + (void)ksm; +#endif static int log_madvise_1 = 1; #ifdef MADV_MERGEABLE static int log_madvise_2 = 1, log_madvise_3 = 1; @@ -1060,17 +1074,6 @@ char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len) { return s; } -char *strncpyz(char *dst, const char *src, size_t n) { - char *p = dst; - - while (*src && n--) - *dst++ = *src++; - - *dst = '\0'; - - return p; -} - int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args) { int size = vsnprintf(dst, n, fmt, args); @@ -1104,7 +1107,17 @@ long get_system_cpus(void) { #ifdef __APPLE__ int32_t tmp_processors; - if (unlikely(GETSYSCTL("hw.logicalcpu", tmp_processors))) { + if (unlikely(GETSYSCTL_BY_NAME("hw.logicalcpu", tmp_processors))) { + error("Assuming system has %d processors.", processors); + } else { + processors = tmp_processors; + } + + return processors; + #elif __FreeBSD__ + int32_t tmp_processors; + + if (unlikely(GETSYSCTL_BY_NAME("hw.ncpu", tmp_processors))) { error("Assuming system has %d processors.", processors); } else { processors = tmp_processors; @@ -1114,7 +1127,7 @@ long get_system_cpus(void) { #else char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/stat", global_host_prefix); + snprintfz(filename, FILENAME_MAX, "%s/proc/stat", netdata_configured_host_prefix); procfile *ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT); if(!ff) { @@ -1143,7 +1156,7 @@ long get_system_cpus(void) { debug(D_SYSTEM, "System has %d processors.", processors); return processors; - #endif /* __APPLE__ */ + #endif /* __APPLE__, __FreeBSD__ */ } pid_t pid_max = 32768; @@ -1153,35 +1166,41 @@ pid_t get_system_pid_max(void) { // we use the number defined in bsd/sys/proc_internal.h in XNU sources pid_max = 99999; return pid_max; + #elif __FreeBSD__ + int32_t tmp_pid_max; + + if (unlikely(GETSYSCTL_BY_NAME("kern.pid_max", tmp_pid_max))) { + pid_max = 99999; + error("Assuming system's maximum pid is %d.", pid_max); + } else { + pid_max = tmp_pid_max; + } + + return pid_max; #else + static char read = 0; + if(unlikely(read)) return pid_max; + read = 1; + char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", global_host_prefix); - procfile *ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT); - if(!ff) { - error("Cannot open file '%s'. Assuming system supports %d pids.", filename, pid_max); - return pid_max; - } + snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", netdata_configured_host_prefix); - ff = procfile_readall(ff); - if(!ff) { - error("Cannot read file '%s'. Assuming system supports %d pids.", filename, pid_max); + unsigned long long max = 0; + if(read_single_number_file(filename, &max) != 0) { + error("Cannot open file '%s'. Assuming system supports %d pids.", filename, pid_max); return pid_max; } - pid_max = (pid_t)str2i(procfile_lineword(ff, 0, 0)); - if(!pid_max) { - procfile_close(ff); - pid_max = 32768; + if(!max) { error("Cannot parse file '%s'. Assuming system supports %d pids.", filename, pid_max); return pid_max; } - procfile_close(ff); - debug(D_SYSTEM, "System supports %d pids.", pid_max); + pid_max = (pid_t) max; return pid_max; - #endif /* __APPLE__ */ + #endif /* __APPLE__, __FreeBSD__ */ } unsigned int hz; diff --git a/src/common.h b/src/common.h index e38e95b48..b82c078fa 100644 --- a/src/common.h +++ b/src/common.h @@ -67,9 +67,9 @@ #include <syslog.h> #include <sys/mman.h> -#if !(defined(__FreeBSD__) || defined(__APPLE__)) +#ifdef HAVE_SYS_PRCTL_H #include <sys/prctl.h> -#endif /* __FreeBSD__ || __APPLE__*/ +#endif #include <sys/resource.h> #include <sys/socket.h> @@ -109,6 +109,10 @@ #include <zlib.h> #endif +#ifdef HAVE_CAPABILITY +#include <sys/capability.h> +#endif + // ---------------------------------------------------------------------------- // netdata common definitions @@ -164,10 +168,11 @@ // ---------------------------------------------------------------------------- // netdata include files -#include "simple_pattern.h" -#include "avl.h" #include "clocks.h" #include "log.h" +#include "locks.h" +#include "simple_pattern.h" +#include "avl.h" #include "global_statistics.h" #include "storage_number.h" #include "web_buffer.h" @@ -184,21 +189,26 @@ #include "plugin_nfacct.h" #if defined(__FreeBSD__) +#include <pthread_np.h> #include "plugin_freebsd.h" +#define NETDATA_OS_TYPE "freebsd" #elif defined(__APPLE__) #include "plugin_macos.h" +#define NETDATA_OS_TYPE "macos" #else #include "plugin_proc.h" #include "plugin_proc_diskspace.h" +#define NETDATA_OS_TYPE "linux" #endif /* __FreeBSD__, __APPLE__*/ -#include "plugin_tc.h" -#include "plugins_d.h" #include "socket.h" #include "eval.h" #include "health.h" #include "rrd.h" +#include "plugin_tc.h" +#include "plugins_d.h" #include "rrd2json.h" +#include "rrd2json_api_old.h" #include "web_client.h" #include "web_server.h" #include "registry.h" @@ -209,6 +219,19 @@ #include "backends.h" #include "inlined.h" #include "adaptive_resortable_list.h" +#include "rrdpush.h" +#include "web_api_v1.h" +#include "web_api_old.h" + +extern char *netdata_configured_hostname; +extern char *netdata_configured_config_dir; +extern char *netdata_configured_log_dir; +extern char *netdata_configured_plugins_dir; +extern char *netdata_configured_web_dir; +extern char *netdata_configured_cache_dir; +extern char *netdata_configured_varlib_dir; +extern char *netdata_configured_home_dir; +extern char *netdata_configured_host_prefix; extern void netdata_fix_chart_id(char *s); extern void netdata_fix_chart_name(char *s); @@ -217,7 +240,6 @@ 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, ...) PRINTFLIKE(3, 4); @@ -249,7 +271,6 @@ extern int savememory(const char *filename, void *mem, size_t size); extern int fd_is_valid(int fd); -extern char *global_host_prefix; extern int enable_ksm; extern pid_t gettid(void); @@ -268,6 +289,10 @@ extern pid_t get_system_pid_max(void); extern unsigned int hz; extern void get_system_HZ(void); +extern volatile sig_atomic_t netdata_exit; +extern const char *os_type; + +extern const char *program_version; /* fix for alpine linux */ #ifndef RUSAGE_THREAD diff --git a/src/daemon.c b/src/daemon.c index 4fd8ca5e5..42b04c401 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -27,7 +27,7 @@ void sig_handler_save(int signo) if(signo) { error_log_limit_unlimited(); info("Received signal %d to save the database...", signo); - rrdset_save_all(); + rrdhost_save_all(); error_log_limit_reset(); } } @@ -84,8 +84,8 @@ int become_user(const char *username, int pid_fd) uid_t uid = pw->pw_uid; gid_t gid = pw->pw_gid; - create_needed_dir(CACHE_DIR, uid, gid); - create_needed_dir(VARLIB_DIR, uid, gid); + create_needed_dir(netdata_configured_cache_dir, uid, gid); + create_needed_dir(netdata_configured_varlib_dir, uid, gid); if(pidfile[0]) { if(chown(pidfile, uid, gid) == -1) @@ -155,13 +155,15 @@ int become_user(const char *username, int pid_fd) return(0); } -void oom_score_adj(int score) { +static void oom_score_adj(void) { + int score = (int)config_get_number(CONFIG_SECTION_GLOBAL, "OOM score", 1000); + 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; + if(len > 0 && write(fd, buf, (size_t)len) == len) done = 1; close(fd); } @@ -171,23 +173,130 @@ void oom_score_adj(int score) { debug(D_SYSTEM, "Adjusted my Out-Of-Memory score to %d.", score); } -int sched_setscheduler_idle(void) { +static void process_nice_level(void) { +#ifdef HAVE_NICE + int nice_level = (int)config_get_number(CONFIG_SECTION_GLOBAL, "process nice level", 19); + if(nice(nice_level) == -1) error("Cannot set netdata CPU nice level to %d.", nice_level); + else debug(D_SYSTEM, "Set netdata nice level to %d.", nice_level); +#endif // HAVE_NICE +}; + +#ifdef HAVE_SCHED_SETSCHEDULER + +#define SCHED_FLAG_NONE 0x00 +#define SCHED_FLAG_PRIORITY_CONFIGURABLE 0x01 // the priority is user configurable +#define SCHED_FLAG_KEEP_AS_IS 0x04 // do not attempt to set policy, priority or nice() +#define SCHED_FLAG_USE_NICE 0x08 // use nice() after setting this policy + +struct sched_def { + char *name; + int policy; + int priority; + uint8_t flags; +} scheduler_defaults[] = { + + // the order of array members is important! + // the first defined is the default used by netdata + + // the available members are important too! + // these are all the possible scheduling policies supported by netdata + #ifdef SCHED_IDLE - const struct sched_param param = { - .sched_priority = 0 - }; + { "idle", SCHED_IDLE, 0, SCHED_FLAG_NONE }, +#endif - int i = sched_setscheduler(0, SCHED_IDLE, ¶m); - if(i != 0) - error("Cannot adjust my scheduling priority to IDLE."); - else - debug(D_SYSTEM, "Adjusted my scheduling priority to IDLE."); +#ifdef SCHED_OTHER + { "nice", SCHED_OTHER, 0, SCHED_FLAG_USE_NICE }, + { "other", SCHED_OTHER, 0, SCHED_FLAG_USE_NICE }, +#endif - return i; -#else - return -1; +#ifdef SCHED_RR + { "rr", SCHED_RR, 0, SCHED_FLAG_PRIORITY_CONFIGURABLE }, +#endif + +#ifdef SCHED_FIFO + { "fifo", SCHED_FIFO, 0, SCHED_FLAG_PRIORITY_CONFIGURABLE }, +#endif + +#ifdef SCHED_BATCH + { "batch", SCHED_BATCH, 0, SCHED_FLAG_USE_NICE }, +#endif + + // do not change the scheduling priority + { "keep", 0, 0, SCHED_FLAG_KEEP_AS_IS }, + { "none", 0, 0, SCHED_FLAG_KEEP_AS_IS }, + + // array termination + { NULL, 0, 0, 0 } +}; + +static void sched_setscheduler_set(void) { + + if(scheduler_defaults[0].name) { + const char *name = scheduler_defaults[0].name; + int policy = scheduler_defaults[0].policy, priority = scheduler_defaults[0].priority; + uint8_t flags = scheduler_defaults[0].flags; + int found = 0; + + // read the configuration + name = config_get(CONFIG_SECTION_GLOBAL, "process scheduling policy", name); + int i; + for(i = 0 ; scheduler_defaults[i].name ; i++) { + if(!strcmp(name, scheduler_defaults[i].name)) { + found = 1; + priority = scheduler_defaults[i].priority; + flags = scheduler_defaults[i].flags; + + if(flags & SCHED_FLAG_KEEP_AS_IS) + return; + + if(flags & SCHED_FLAG_PRIORITY_CONFIGURABLE) + priority = (int)config_get_number(CONFIG_SECTION_GLOBAL, "process scheduling priority", priority); + +#ifdef HAVE_SCHED_GET_PRIORITY_MIN + if(priority < sched_get_priority_min(policy)) { + error("scheduler %s priority %d is below the minimum %d. Using the minimum.", name, priority, sched_get_priority_min(policy)); + priority = sched_get_priority_min(policy); + } +#endif +#ifdef HAVE_SCHED_GET_PRIORITY_MAX + if(priority > sched_get_priority_max(policy)) { + error("scheduler %s priority %d is above the maximum %d. Using the maximum.", name, priority, sched_get_priority_max(policy)); + priority = sched_get_priority_max(policy); + } #endif + break; + } + } + + if(!found) { + error("Unknown scheduling policy %s - falling back to nice()", name); + goto fallback; + } + + const struct sched_param param = { + .sched_priority = priority + }; + + i = sched_setscheduler(0, policy, ¶m); + if(i != 0) { + error("Cannot adjust netdata scheduling policy to %s (%d), with priority %d. Falling back to nice", name, policy, priority); + } + else { + debug(D_SYSTEM, "Adjusted netdata scheduling policy to %s (%d), with priority %d.", name, policy, priority); + if(!(flags & SCHED_FLAG_USE_NICE)) + return; + } + } + +fallback: + process_nice_level(); } +#else +static void sched_setscheduler_set(void) { + process_nice_level(); +} +#endif int become_daemon(int dont_fork, const char *user) { @@ -239,13 +348,10 @@ int become_daemon(int dont_fork, const char *user) umask(0007); // adjust my Out-Of-Memory score - oom_score_adj(1000); + oom_score_adj(); // never become a problem - if(sched_setscheduler_idle() != 0) { - if(nice(19) == -1) error("Cannot lower my CPU priority."); - else debug(D_SYSTEM, "Set my nice value to 19."); - } + sched_setscheduler_set(); if(user && *user) { if(become_user(user, pidfd) != 0) { @@ -254,8 +360,8 @@ int become_daemon(int dont_fork, const char *user) else debug(D_SYSTEM, "Successfully became user '%s'.", user); } else { - create_needed_dir(CACHE_DIR, getuid(), getgid()); - create_needed_dir(VARLIB_DIR, getuid(), getgid()); + create_needed_dir(netdata_configured_cache_dir, getuid(), getgid()); + create_needed_dir(netdata_configured_varlib_dir, getuid(), getgid()); } if(pidfd != -1) diff --git a/src/dictionary.c b/src/dictionary.c index fb9efeedb..512b4bbe6 100644 --- a/src/dictionary.c +++ b/src/dictionary.c @@ -31,21 +31,21 @@ static inline void NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict) { static inline void dictionary_read_lock(DICTIONARY *dict) { if(likely(dict->rwlock)) { // debug(D_DICTIONARY, "Dictionary READ lock"); - pthread_rwlock_rdlock(dict->rwlock); + netdata_rwlock_rdlock(dict->rwlock); } } static inline void dictionary_write_lock(DICTIONARY *dict) { if(likely(dict->rwlock)) { // debug(D_DICTIONARY, "Dictionary WRITE lock"); - pthread_rwlock_wrlock(dict->rwlock); + netdata_rwlock_wrlock(dict->rwlock); } } static inline void dictionary_unlock(DICTIONARY *dict) { if(likely(dict->rwlock)) { // debug(D_DICTIONARY, "Dictionary UNLOCK lock"); - pthread_rwlock_unlock(dict->rwlock); + netdata_rwlock_unlock(dict->rwlock); } } @@ -135,8 +135,8 @@ DICTIONARY *dictionary_create(uint8_t flags) { dict->stats = callocz(1, sizeof(struct dictionary_stats)); if(!(flags & DICTIONARY_FLAG_SINGLE_THREADED)) { - dict->rwlock = callocz(1, sizeof(pthread_rwlock_t)); - pthread_rwlock_init(dict->rwlock, NULL); + dict->rwlock = callocz(1, sizeof(netdata_rwlock_t)); + netdata_rwlock_init(dict->rwlock); } avl_init(&dict->values_index, name_value_compare); @@ -158,8 +158,10 @@ void dictionary_destroy(DICTIONARY *dict) { if(dict->stats) freez(dict->stats); - if(dict->rwlock) + if(dict->rwlock) { + netdata_rwlock_destroy(dict->rwlock); freez(dict->rwlock); + } freez(dict); } diff --git a/src/dictionary.h b/src/dictionary.h index 6bebbfa85..f028dbb30 100644 --- a/src/dictionary.h +++ b/src/dictionary.h @@ -24,7 +24,7 @@ typedef struct dictionary { uint8_t flags; struct dictionary_stats *stats; - pthread_rwlock_t *rwlock; + netdata_rwlock_t *rwlock; } DICTIONARY; #define DICTIONARY_FLAG_DEFAULT 0x00000000 diff --git a/src/freebsd_sysctl.c b/src/freebsd_sysctl.c index 9400089db..965c1cbbf 100644 --- a/src/freebsd_sysctl.c +++ b/src/freebsd_sysctl.c @@ -1,1116 +1,1309 @@ #include "common.h" -// NEEDED BY: struct vmtotal, struct vmmeter #include <sys/vmmeter.h> -// NEEDED BY: struct devstat #include <sys/devicestat.h> -// NEEDED BY: struct xswdev +#include <sys/mount.h> #include <vm/vm_param.h> -// NEEDED BY: struct semid_kernel, struct shmid_kernel, struct msqid_kernel + #define _KERNEL #include <sys/sem.h> #include <sys/shm.h> #include <sys/msg.h> #undef _KERNEL -// NEEDED BY: struct sysctl_netisr_workstream, struct sysctl_netisr_work + #include <net/netisr.h> -// NEEDED BY: struct ifaddrs, getifaddrs() #include <net/if.h> #include <ifaddrs.h> -// NEEDED BY do_tcp... -#include <netinet/tcp_var.h> -#include <netinet/tcp_fsm.h> -// NEEDED BY do_udp..., do_ip... -#include <netinet/ip_var.h> -// NEEDED BY do_udp... -#include <netinet/udp.h> -#include <netinet/udp_var.h> -// NEEDED BY do_icmp... + #include <netinet/ip.h> +#include <netinet/ip_var.h> #include <netinet/ip_icmp.h> #include <netinet/icmp_var.h> -// NEEDED BY do_ip6... #include <netinet6/ip6_var.h> -// NEEDED BY do_icmp6... #include <netinet/icmp6.h> -// NEEDED BY do_space, do_inodes -#include <sys/mount.h> -// NEEDED BY do_uptime -#include <time.h> +#include <netinet/tcp_var.h> +#include <netinet/tcp_fsm.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> + +// -------------------------------------------------------------------------------------------------------------------- +// common definitions and variables #define KILO_FACTOR 1024 #define MEGA_FACTOR 1048576 // 1024 * 1024 #define GIGA_FACTOR 1073741824 // 1024 * 1024 * 1024 -// NEEDED BY: do_disk_io -#define RRD_TYPE_DISK "disk" +#define MAX_INT_DIGITS 10 // maximum number of digits for int -// FreeBSD calculates load averages once every 5 seconds -#define MIN_LOADAVG_UPDATE_EVERY 5 +int system_pagesize = PAGE_SIZE; +int number_of_cpus = 1; -// NEEDED BY: do_bandwidth -#define IFA_DATA(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s) +// -------------------------------------------------------------------------------------------------------------------- +// FreeBSD plugin initialization -int do_freebsd_sysctl(int update_every, usec_t dt) { - (void)dt; - - static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1, - do_loadavg = -1, do_all_processes = -1, do_disk_io = -1, do_swap = -1, do_ram = -1, do_swapio = -1, - do_pgfaults = -1, do_committed = -1, do_ipc_semaphores = -1, do_ipc_shared_mem = -1, do_ipc_msg_queues = -1, - do_dev_intr = -1, do_soft_intr = -1, do_netisr = -1, do_netisr_per_core = -1, do_bandwidth = -1, - do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1, - do_ecn = -1, do_tcpext_syscookies = -1, do_tcpext_ofo = -1, do_tcpext_connaborts = -1, - do_udp_packets = -1, do_udp_errors = -1, do_icmp_packets = -1, do_icmpmsg = -1, - do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1, - do_ip6_packets = -1, do_ip6_fragsout = -1, do_ip6_fragsin = -1, do_ip6_errors = -1, - do_icmp6 = -1, do_icmp6_redir = -1, do_icmp6_errors = -1, do_icmp6_echos = -1, do_icmp6_router = -1, - do_icmp6_neighbor = -1, do_icmp6_types = -1, do_space = -1, do_inodes = -1, do_uptime = -1; - - if (unlikely(do_cpu == -1)) { - do_cpu = config_get_boolean("plugin:freebsd:sysctl", "cpu utilization", 1); - do_cpu_cores = config_get_boolean("plugin:freebsd:sysctl", "per cpu core utilization", 1); - do_interrupts = config_get_boolean("plugin:freebsd:sysctl", "cpu interrupts", 1); - do_dev_intr = config_get_boolean("plugin:freebsd:sysctl", "device interrupts", 1); - do_soft_intr = config_get_boolean("plugin:freebsd:sysctl", "software interrupts", 1); - do_context = config_get_boolean("plugin:freebsd:sysctl", "context switches", 1); - do_forks = config_get_boolean("plugin:freebsd:sysctl", "processes started", 1); - do_processes = config_get_boolean("plugin:freebsd:sysctl", "processes running", 1); - do_loadavg = config_get_boolean("plugin:freebsd:sysctl", "enable load average", 1); - do_all_processes = config_get_boolean("plugin:freebsd:sysctl", "enable total processes", 1); - do_disk_io = config_get_boolean("plugin:freebsd:sysctl", "stats for all disks", 1); - do_swap = config_get_boolean("plugin:freebsd:sysctl", "system swap", 1); - do_ram = config_get_boolean("plugin:freebsd:sysctl", "system ram", 1); - do_swapio = config_get_boolean("plugin:freebsd:sysctl", "swap i/o", 1); - do_pgfaults = config_get_boolean("plugin:freebsd:sysctl", "memory page faults", 1); - do_committed = config_get_boolean("plugin:freebsd:sysctl", "committed memory", 1); - do_ipc_semaphores = config_get_boolean("plugin:freebsd:sysctl", "ipc semaphores", 1); - do_ipc_shared_mem = config_get_boolean("plugin:freebsd:sysctl", "ipc shared memory", 1); - do_ipc_msg_queues = config_get_boolean("plugin:freebsd:sysctl", "ipc message queues", 1); - do_netisr = config_get_boolean("plugin:freebsd:sysctl", "netisr", 1); - do_netisr_per_core = config_get_boolean("plugin:freebsd:sysctl", "netisr per core", 1); - do_bandwidth = config_get_boolean("plugin:freebsd:sysctl", "bandwidth", 1); - do_tcp_sockets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP connections", 1); - do_tcp_packets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP packets", 1); - do_tcp_errors = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP errors", 1); - do_tcp_handshake = config_get_boolean("plugin:freebsd:sysctl", "ipv4 TCP handshake issues", 1); - do_ecn = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ECN packets", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_syscookies = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_ofo = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_connaborts = config_get_boolean_ondemand("plugin:freebsd:sysctl", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND); - do_udp_packets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 UDP packets", 1); - do_udp_errors = config_get_boolean("plugin:freebsd:sysctl", "ipv4 UDP errors", 1); - do_icmp_packets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 ICMP packets", 1); - do_icmpmsg = config_get_boolean("plugin:freebsd:sysctl", "ipv4 ICMP messages", 1); - do_ip_packets = config_get_boolean("plugin:freebsd:sysctl", "ipv4 packets", 1); - do_ip_fragsout = config_get_boolean("plugin:freebsd:sysctl", "ipv4 fragments sent", 1); - do_ip_fragsin = config_get_boolean("plugin:freebsd:sysctl", "ipv4 fragments assembly", 1); - do_ip_errors = config_get_boolean("plugin:freebsd:sysctl", "ipv4 errors", 1); - do_ip6_packets = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND); - do_ip6_fragsout = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND); - do_ip6_fragsin = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND); - do_ip6_errors = config_get_boolean_ondemand("plugin:freebsd:sysctl", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6 = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_redir = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_errors = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp errors", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_echos = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp echos", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_router = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp router", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_neighbor = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_types = config_get_boolean_ondemand("plugin:freebsd:sysctl", "icmp types", CONFIG_ONDEMAND_ONDEMAND); - do_space = config_get_boolean("plugin:freebsd:sysctl", "space usage for all disks", 1); - do_inodes = config_get_boolean("plugin:freebsd:sysctl", "inodes usage for all disks", 1); - do_uptime = config_get_boolean("plugin:macos:sysctl", "system uptime", 1); +int freebsd_plugin_init() +{ + system_pagesize = getpagesize(); + if (system_pagesize <= 0) { + error("FREEBSD: can't get system page size"); + return 1; } - RRDSET *st; - RRDDIM *rd; - - int system_pagesize = getpagesize(); // wouldn't it be better to get value directly from hw.pagesize? - int i, n; - void *p; - int common_error = 0; - size_t size; - char title[4096 + 1]; - - // NEEDED BY: do_loadavg - static usec_t last_loadavg_usec = 0; - struct loadavg sysload; - - // NEEDED BY: do_cpu, do_cpu_cores - long cp_time[CPUSTATES]; - - // NEEDED BY: du_cpu_cores, do_netisr, do_netisr_per_core - int ncpus; - - // NEEDED BY: do_cpu_cores - static long *pcpu_cp_time = NULL; - char cpuid[8]; // no more than 4 digits expected - - // NEEDED BY: do_all_processes, do_processes - struct vmtotal vmtotal_data; - - // NEEDED BY: do_context, do_forks - u_int u_int_data; - - // NEEDED BY: do_interrupts - size_t intrcnt_size; - unsigned long nintr = 0; - static unsigned long *intrcnt = NULL; - static char *intrnames = NULL; - unsigned long long totalintr = 0; - - // NEEDED BY: do_disk_io - #define BINTIME_SCALE 5.42101086242752217003726400434970855712890625e-17 // this is 1000/2^64 - int numdevs; - static void *devstat_data = NULL; - struct devstat *dstat; - char disk[DEVSTAT_NAME_LEN + 10 + 1]; // 10 - maximum number of digits for int - struct cur_dstat { - collected_number duration_read_ms; - collected_number duration_write_ms; - collected_number busy_time_ms; - } cur_dstat; - struct prev_dstat { - collected_number bytes_read; - collected_number bytes_write; - collected_number operations_read; - collected_number operations_write; - collected_number duration_read_ms; - collected_number duration_write_ms; - collected_number busy_time_ms; - } prev_dstat; - - // NEEDED BY: do_swap - size_t mibsize; - int mib[3]; // CTL_MAXNAME = 24 maximum mib components (sysctl.h) - struct xswdev xsw; - struct total_xsw { - collected_number bytes_used; - collected_number bytes_total; - } total_xsw = {0, 0}; - - // NEEDED BY: do_swapio, do_ram - struct vmmeter vmmeter_data; - - // NEEDED BY: do_ram - int vfs_bufspace_count; - - // NEEDED BY: do_ipc_semaphores - struct ipc_sem { - int semmni; - collected_number sets; - collected_number semaphores; - } ipc_sem = {0, 0, 0}; - static struct semid_kernel *ipc_sem_data = NULL; - - // NEEDED BY: do_ipc_shared_mem - struct ipc_shm { - u_long shmmni; - collected_number segs; - collected_number segsize; - } ipc_shm = {0, 0, 0}; - static struct shmid_kernel *ipc_shm_data = NULL; - - // NEEDED BY: do_ipc_msg_queues - struct ipc_msq { - int msgmni; - collected_number queues; - collected_number messages; - collected_number usedsize; - collected_number allocsize; - } ipc_msq = {0, 0, 0, 0, 0}; - static struct msqid_kernel *ipc_msq_data = NULL; + if (unlikely(GETSYSCTL_BY_NAME("kern.smp.cpus", number_of_cpus))) { + error("FREEBSD: can't get number of cpus"); + return 1; + } - // NEEDED BY: do_netisr, do_netisr_per_core - size_t netisr_workstream_size; - size_t netisr_work_size; - unsigned long num_netisr_workstreams = 0, num_netisr_works = 0; - static struct sysctl_netisr_workstream *netisr_workstream = NULL; - static struct sysctl_netisr_work *netisr_work = NULL; - static struct netisr_stats { - collected_number dispatched; - collected_number hybrid_dispatched; - collected_number qdrops; - collected_number queued; - } *netisr_stats = NULL; - char netstat_cpuid[21]; // no more than 4 digits expected + if (unlikely(!number_of_cpus)) { + error("FREEBSD: wrong number of cpus"); + return 1; + } - // NEEDED BY: do_bandwidth - struct ifaddrs *ifa, *ifap; - struct iftot { - u_long ift_ibytes; - u_long ift_obytes; - } iftot = {0, 0}; + return 0; +} - // NEEDED BY: do_tcp... - struct tcpstat tcpstat; - uint64_t tcps_states[TCP_NSTATES]; +// -------------------------------------------------------------------------------------------------------------------- +// vm.loadavg - // NEEDED BY: do_udp... - struct udpstat udpstat; +// FreeBSD calculates load averages once every 5 seconds +#define MIN_LOADAVG_UPDATE_EVERY 5 - // NEEDED BY: do_icmp... - struct icmpstat icmpstat; - struct icmp_total { - u_long msgs_in; - u_long msgs_out; - } icmp_total = {0, 0}; +int do_vm_loadavg(int update_every, usec_t dt){ + static usec_t next_loadavg_dt = 0; - // NEEDED BY: do_ip... - struct ipstat ipstat; + if (next_loadavg_dt <= dt) { + static int mib[2] = {0, 0}; + struct loadavg sysload; - // NEEDED BY: do_ip6... - struct ip6stat ip6stat; + if (unlikely(GETSYSCTL_SIMPLE("vm.loadavg", mib, sysload))) { + error("DISABLED: system.load chart"); + error("DISABLED: vm.loadavg module"); + return 1; + } else { - // NEEDED BY: do_icmp6... - struct icmp6stat icmp6stat; - struct icmp6_total { - u_long msgs_in; - u_long msgs_out; - } icmp6_total = {0, 0}; + // -------------------------------------------------------------------- - // NEEDED BY: do_space, do_inodes - struct statfs *mntbuf; - int mntsize; - char mntonname[MNAMELEN + 1]; + static RRDSET *st = NULL; + static RRDDIM *rd_load1 = NULL, *rd_load2 = NULL, *rd_load3 = NULL; - // NEEDED BY: do_uptime - struct timespec boot_time, cur_time; + if (unlikely(!st)) { + st = rrdset_create_localhost("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 + ); + rd_load1 = rrddim_add(st, "load1", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rd_load2 = rrddim_add(st, "load5", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rd_load3 = rrddim_add(st, "load15", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + } else + rrdset_next(st); + + rrddim_set_by_pointer(st, rd_load1, (collected_number) ((double) sysload.ldavg[0] / sysload.fscale * 1000)); + rrddim_set_by_pointer(st, rd_load2, (collected_number) ((double) sysload.ldavg[1] / sysload.fscale * 1000)); + rrddim_set_by_pointer(st, rd_load3, (collected_number) ((double) sysload.ldavg[2] / sysload.fscale * 1000)); + rrdset_done(st); - // -------------------------------------------------------------------- + next_loadavg_dt = st->update_every * USEC_PER_SEC; + } + } + else + next_loadavg_dt -= dt; - if (last_loadavg_usec <= dt) { - if (likely(do_loadavg)) { - if (unlikely(GETSYSCTL("vm.loadavg", sysload))) { - do_loadavg = 0; - error("DISABLED: system.load"); - } else { + return 0; +} - st = rrdset_find_bytype("system", "load"); - if (unlikely(!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); +// -------------------------------------------------------------------------------------------------------------------- +// vm.vmtotal - rrddim_set(st, "load1", (collected_number) ((double)sysload.ldavg[0] / sysload.fscale * 1000)); - rrddim_set(st, "load5", (collected_number) ((double)sysload.ldavg[1] / sysload.fscale * 1000)); - rrddim_set(st, "load15", (collected_number) ((double)sysload.ldavg[2] / sysload.fscale * 1000)); - rrdset_done(st); - } - } +int do_vm_vmtotal(int update_every, usec_t dt) { + (void)dt; + static int do_all_processes = -1, do_processes = -1, do_committed = -1; - last_loadavg_usec = st->update_every * USEC_PER_SEC; + if (unlikely(do_all_processes == -1)) { + do_all_processes = config_get_boolean("plugin:freebsd:vm.vmtotal", "enable total processes", 1); + do_processes = config_get_boolean("plugin:freebsd:vm.vmtotal", "processes running", 1); + do_committed = config_get_boolean("plugin:freebsd:vm.vmtotal", "committed memory", 1); } - else last_loadavg_usec -= dt; - - // -------------------------------------------------------------------- if (likely(do_all_processes | do_processes | do_committed)) { - if (unlikely(GETSYSCTL("vm.vmtotal", vmtotal_data))) { + static int mib[2] = {0, 0}; + struct vmtotal vmtotal_data; + + if (unlikely(GETSYSCTL_SIMPLE("vm.vmtotal", mib, vmtotal_data))) { do_all_processes = 0; - error("DISABLED: system.active_processes"); + error("DISABLED: system.active_processes chart"); do_processes = 0; - error("DISABLED: system.processes"); + error("DISABLED: system.processes chart"); do_committed = 0; - error("DISABLED: mem.committed"); + error("DISABLED: mem.committed chart"); + error("DISABLED: vm.vmtotal module"); + return 1; } else { + + // -------------------------------------------------------------------- + if (likely(do_all_processes)) { + static RRDSET *st = NULL; + static RRDDIM *rd = NULL; - st = rrdset_find_bytype("system", "active_processes"); if (unlikely(!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); + st = rrdset_create_localhost("system", + "active_processes", + NULL, + "processes", + NULL, + "System Active Processes", + "processes", + 750, + update_every, + RRDSET_TYPE_LINE + ); + rd = rrddim_add(st, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); - rrddim_set(st, "active", (vmtotal_data.t_rq + vmtotal_data.t_dw + vmtotal_data.t_pw + vmtotal_data.t_sl + vmtotal_data.t_sw)); + rrddim_set_by_pointer(st, rd, (vmtotal_data.t_rq + vmtotal_data.t_dw + vmtotal_data.t_pw + vmtotal_data.t_sl + vmtotal_data.t_sw)); rrdset_done(st); } // -------------------------------------------------------------------- if (likely(do_processes)) { + static RRDSET *st = NULL; + static RRDDIM *rd_running = NULL, *rd_blocked = NULL; - 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); + st = rrdset_create_localhost("system", + "processes", + NULL, + "processes", + NULL, + "System Processes", + "processes", + 600, + update_every, + RRDSET_TYPE_LINE + ); + + rd_running = rrddim_add(st, "running", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_blocked = rrddim_add(st, "blocked", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); - rrddim_set(st, "running", vmtotal_data.t_rq); - rrddim_set(st, "blocked", (vmtotal_data.t_dw + vmtotal_data.t_pw)); + rrddim_set_by_pointer(st, rd_running, vmtotal_data.t_rq); + rrddim_set_by_pointer(st, rd_blocked, (vmtotal_data.t_dw + vmtotal_data.t_pw)); rrdset_done(st); } // -------------------------------------------------------------------- if (likely(do_committed)) { - st = rrdset_find("mem.committed"); - if (unlikely(!st)) { - st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA); - st->isdetail = 1; + static RRDSET *st = NULL; + static RRDDIM *rd = NULL; - rrddim_add(st, "Committed_AS", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + if (unlikely(!st)) { + st = rrdset_create_localhost("mem", + "committed", + NULL, + "system", + NULL, + "Committed (Allocated) Memory", + "MB", + 5000, + update_every, + RRDSET_TYPE_AREA + ); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd = rrddim_add(st, "Committed_AS", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); - rrddim_set(st, "Committed_AS", vmtotal_data.t_rm); + rrddim_set_by_pointer(st, rd, vmtotal_data.t_rm); rrdset_done(st); } } + } else { + error("DISABLED: vm.vmtotal module"); + return 1; } - // -------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// kern.cp_time + +int do_kern_cp_time(int update_every, usec_t dt) { + (void)dt; - if (likely(do_cpu)) { - if (unlikely(CPUSTATES != 5)) { - error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES); - do_cpu = 0; - error("DISABLED: system.cpu"); + if (unlikely(CPUSTATES != 5)) { + error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES); + error("DISABLED: system.cpu chart"); + error("DISABLED: kern.cp_time module"); + return 1; + } else { + static int mib[2] = {0, 0}; + long cp_time[CPUSTATES]; + + if (unlikely(GETSYSCTL_SIMPLE("kern.cp_time", mib, cp_time))) { + error("DISABLED: system.cpu chart"); + error("DISABLED: kern.cp_time module"); + return 1; } else { - if (unlikely(GETSYSCTL("kern.cp_time", cp_time))) { - do_cpu = 0; - error("DISABLED: system.cpu"); - } else { - st = rrdset_find_bytype("system", "cpu"); - if (unlikely(!st)) { - st = rrdset_create("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED); - - rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_hide(st, "idle"); - } - else rrdset_next(st); + // -------------------------------------------------------------------- - rrddim_set(st, "user", cp_time[0]); - rrddim_set(st, "nice", cp_time[1]); - rrddim_set(st, "system", cp_time[2]); - rrddim_set(st, "interrupt", cp_time[3]); - rrddim_set(st, "idle", cp_time[4]); - rrdset_done(st); + static RRDSET *st = NULL; + static RRDDIM *rd_nice = NULL, *rd_system = NULL, *rd_user = NULL, *rd_interrupt = NULL, *rd_idle = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "cpu", + NULL, + "cpu", + "system.cpu", + "Total CPU utilization", + "percentage", + 100, update_every, + RRDSET_TYPE_STACKED + ); + + rd_nice = rrddim_add(st, "nice", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_system = rrddim_add(st, "system", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_user = rrddim_add(st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_interrupt = rrddim_add(st, "interrupt", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_idle = rrddim_add(st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_hide(st, "idle"); } + else rrdset_next(st); + + rrddim_set_by_pointer(st, rd_nice, cp_time[1]); + rrddim_set_by_pointer(st, rd_system, cp_time[2]); + rrddim_set_by_pointer(st, rd_user, cp_time[0]); + rrddim_set_by_pointer(st, rd_interrupt, cp_time[3]); + rrddim_set_by_pointer(st, rd_idle, cp_time[4]); + rrdset_done(st); } } - // -------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// kern.cp_times + +int do_kern_cp_times(int update_every, usec_t dt) { + (void)dt; - if (likely(do_cpu_cores)) { - if (unlikely(CPUSTATES != 5)) { - error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES); - do_cpu_cores = 0; - error("DISABLED: cpu.cpuXX"); + if (unlikely(CPUSTATES != 5)) { + error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES); + error("DISABLED: cpu.cpuXX charts"); + error("DISABLED: kern.cp_times module"); + return 1; + } else { + static int mib[2] = {0, 0}; + long cp_time[CPUSTATES]; + static long *pcpu_cp_time = NULL; + + pcpu_cp_time = reallocz(pcpu_cp_time, sizeof(cp_time) * number_of_cpus); + if (unlikely(GETSYSCTL_WSIZE("kern.cp_times", mib, pcpu_cp_time, sizeof(cp_time) * number_of_cpus))) { + error("DISABLED: cpu.cpuXX charts"); + error("DISABLED: kern.cp_times module"); + return 1; } else { - if (unlikely(GETSYSCTL("kern.smp.cpus", ncpus))) { - do_cpu_cores = 0; - error("DISABLED: cpu.cpuXX"); - } else { - pcpu_cp_time = reallocz(pcpu_cp_time, sizeof(cp_time) * ncpus); - for (i = 0; i < ncpus; i++) { - if (unlikely(getsysctl("kern.cp_times", pcpu_cp_time, sizeof(cp_time) * ncpus))) { - do_cpu_cores = 0; - error("DISABLED: cpu.cpuXX"); - break; - } - if (unlikely(ncpus > 9999)) { - error("FREEBSD: There are more than 4 digits in cpu cores number"); - do_cpu_cores = 0; - error("DISABLED: cpu.cpuXX"); - break; - } - snprintfz(cpuid, 8, "cpu%d", i); + // -------------------------------------------------------------------- - st = rrdset_find_bytype("cpu", cpuid); - if (unlikely(!st)) { - st = rrdset_create("cpu", cpuid, NULL, "utilization", "cpu.cpu", "Core utilization", "percentage", 1000, update_every, RRDSET_TYPE_STACKED); - - rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_hide(st, "idle"); - } - else rrdset_next(st); + int i; + static struct cpu_chart { + char cpuid[MAX_INT_DIGITS + 4]; + RRDSET *st; + RRDDIM *rd_user; + RRDDIM *rd_nice; + RRDDIM *rd_system; + RRDDIM *rd_interrupt; + RRDDIM *rd_idle; + } *all_cpu_charts = NULL; + static int old_number_of_cpus = 0; + + if(unlikely(number_of_cpus > old_number_of_cpus)) { + all_cpu_charts = reallocz(all_cpu_charts, sizeof(struct cpu_chart) * number_of_cpus); + memset(&all_cpu_charts[old_number_of_cpus], 0, sizeof(struct cpu_chart) * (number_of_cpus - old_number_of_cpus)); + old_number_of_cpus = number_of_cpus; + } - rrddim_set(st, "user", pcpu_cp_time[i * 5 + 0]); - rrddim_set(st, "nice", pcpu_cp_time[i * 5 + 1]); - rrddim_set(st, "system", pcpu_cp_time[i * 5 + 2]); - rrddim_set(st, "interrupt", pcpu_cp_time[i * 5 + 3]); - rrddim_set(st, "idle", pcpu_cp_time[i * 5 + 4]); - rrdset_done(st); - } + for (i = 0; i < number_of_cpus; i++) { + if (unlikely(!all_cpu_charts[i].st)) { + snprintfz(all_cpu_charts[i].cpuid, MAX_INT_DIGITS, "cpu%d", i); + all_cpu_charts[i].st = rrdset_create_localhost("cpu", + all_cpu_charts[i].cpuid, + NULL, + "utilization", + "cpu.cpu", + "Core utilization", + "percentage", + 1000, + update_every, + RRDSET_TYPE_STACKED + ); + + all_cpu_charts[i].rd_nice = rrddim_add(all_cpu_charts[i].st, "nice", NULL, 1, 1, + RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + all_cpu_charts[i].rd_system = rrddim_add(all_cpu_charts[i].st, "system", NULL, 1, 1, + RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + all_cpu_charts[i].rd_user = rrddim_add(all_cpu_charts[i].st, "user", NULL, 1, 1, + RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + all_cpu_charts[i].rd_interrupt = rrddim_add(all_cpu_charts[i].st, "interrupt", NULL, 1, 1, + RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + all_cpu_charts[i].rd_idle = rrddim_add(all_cpu_charts[i].st, "idle", NULL, 1, 1, + RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_hide(all_cpu_charts[i].st, "idle"); + } else rrdset_next(all_cpu_charts[i].st); + + rrddim_set_by_pointer(all_cpu_charts[i].st, all_cpu_charts[i].rd_nice, pcpu_cp_time[i * 5 + 1]); + rrddim_set_by_pointer(all_cpu_charts[i].st, all_cpu_charts[i].rd_system, pcpu_cp_time[i * 5 + 2]); + rrddim_set_by_pointer(all_cpu_charts[i].st, all_cpu_charts[i].rd_user, pcpu_cp_time[i * 5 + 0]); + rrddim_set_by_pointer(all_cpu_charts[i].st, all_cpu_charts[i].rd_interrupt, pcpu_cp_time[i * 5 + 3]); + rrddim_set_by_pointer(all_cpu_charts[i].st, all_cpu_charts[i].rd_idle, pcpu_cp_time[i * 5 + 4]); + rrdset_done(all_cpu_charts[i].st); } } } - // -------------------------------------------------------------------- + return 0; +} - if (likely(do_interrupts)) { - if (unlikely(sysctlbyname("hw.intrcnt", NULL, &intrcnt_size, NULL, 0) == -1)) { - error("FREEBSD: sysctl(hw.intrcnt...) failed: %s", strerror(errno)); - do_interrupts = 0; - error("DISABLED: system.intr"); +// -------------------------------------------------------------------------------------------------------------------- +// hw.intrcnt + +int do_hw_intcnt(int update_every, usec_t dt) { + (void)dt; + static int mib_hw_intrcnt[2] = {0, 0}; + size_t intrcnt_size = sizeof(mib_hw_intrcnt); + unsigned long i; + + if (unlikely(GETSYSCTL_SIZE("hw.intrcnt", mib_hw_intrcnt, intrcnt_size))) { + error("DISABLED: system.intr chart"); + error("DISABLED: system.interrupts chart"); + error("DISABLED: hw.intrcnt module"); + return 1; + } else { + unsigned long nintr = 0; + static unsigned long *intrcnt = NULL; + unsigned long long totalintr = 0; + + nintr = intrcnt_size / sizeof(u_long); + intrcnt = reallocz(intrcnt, nintr * sizeof(u_long)); + if (unlikely(GETSYSCTL_WSIZE("hw.intrcnt", mib_hw_intrcnt, intrcnt, nintr * sizeof(u_long)))) { + error("DISABLED: system.intr chart"); + error("DISABLED: system.interrupts chart"); + error("DISABLED: hw.intrcnt module"); + return 1; } else { - nintr = intrcnt_size / sizeof(u_long); - intrcnt = reallocz(intrcnt, nintr * sizeof(u_long)); - if (unlikely(getsysctl("hw.intrcnt", intrcnt, nintr * sizeof(u_long)))){ - do_interrupts = 0; - error("DISABLED: system.intr"); - } else { - for (i = 0; i < nintr; i++) - totalintr += intrcnt[i]; + for (i = 0; i < nintr; i++) + totalintr += intrcnt[i]; - st = rrdset_find_bytype("system", "intr"); - if (unlikely(!st)) { - st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "Total Hardware 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); + static RRDSET *st_intr = NULL; + static RRDDIM *rd_intr = NULL; + + if (unlikely(!st_intr)) { + st_intr = rrdset_create_localhost("system", + "intr", + NULL, + "interrupts", + NULL, + "Total Hardware Interrupts", + "interrupts/s", + 900, + update_every, + RRDSET_TYPE_LINE + ); + rrdset_flag_set(st_intr, RRDSET_FLAG_DETAIL); + + rd_intr = rrddim_add(st_intr, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(st_intr); + + rrddim_set_by_pointer(st_intr, rd_intr, totalintr); + rrdset_done(st_intr); - rrddim_set(st, "interrupts", totalintr); - rrdset_done(st); + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + size_t size; + static int mib_hw_intrnames[2] = {0, 0}; + static char *intrnames = NULL; + + size = nintr * (MAXCOMLEN + 1); + intrnames = reallocz(intrnames, size); + if (unlikely(GETSYSCTL_WSIZE("hw.intrnames", mib_hw_intrnames, intrnames, size))) { + error("DISABLED: system.intr chart"); + error("DISABLED: system.interrupts chart"); + error("DISABLED: hw.intrcnt module"); + return 1; + } else { - size = nintr * (MAXCOMLEN +1); - intrnames = reallocz(intrnames, size); - if (unlikely(getsysctl("hw.intrnames", intrnames, size))) { - do_interrupts = 0; - error("DISABLED: system.intr"); - } else { - st = rrdset_find_bytype("system", "interrupts"); - if (unlikely(!st)) - st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", - 1000, update_every, RRDSET_TYPE_STACKED); - else - rrdset_next(st); + // -------------------------------------------------------------------- - for (i = 0; i < nintr; i++) { - p = intrnames + i * (MAXCOMLEN + 1); - if (unlikely((intrcnt[i] != 0) && (*(char*)p != 0))) { - rd = rrddim_find(st, p); - if (unlikely(!rd)) - rd = rrddim_add(st, p, NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_set_by_pointer(st, rd, intrcnt[i]); - } + static RRDSET *st_interrupts = NULL; + RRDDIM *rd_interrupts = NULL; + void *p; + + if (unlikely(!st_interrupts)) + st_interrupts = rrdset_create_localhost("system", + "interrupts", + NULL, + "interrupts", + NULL, + "System interrupts", + "interrupts/s", + 1000, + update_every, + RRDSET_TYPE_STACKED + ); + else + rrdset_next(st_interrupts); + + for (i = 0; i < nintr; i++) { + p = intrnames + i * (MAXCOMLEN + 1); + if (unlikely((intrcnt[i] != 0) && (*(char *) p != 0))) { + rd_interrupts = rrddim_find(st_interrupts, p); + if (unlikely(!rd_interrupts)) + rd_interrupts = rrddim_add(st_interrupts, p, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_set_by_pointer(st_interrupts, rd_interrupts, intrcnt[i]); } - rrdset_done(st); } + rrdset_done(st_interrupts); } } } - // -------------------------------------------------------------------- + return 0; +} - if (likely(do_dev_intr)) { - if (unlikely(GETSYSCTL("vm.stats.sys.v_intr", u_int_data))) { - do_dev_intr = 0; - error("DISABLED: system.dev_intr"); - } else { +// -------------------------------------------------------------------------------------------------------------------- +// vm.stats.sys.v_intr - st = rrdset_find_bytype("system", "dev_intr"); - if (unlikely(!st)) { - st = rrdset_create("system", "dev_intr", NULL, "interrupts", NULL, "Device Interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_LINE); +int do_vm_stats_sys_v_intr(int update_every, usec_t dt) { + (void)dt; + static int mib[4] = {0, 0, 0, 0}; + u_int int_number; - rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + if (unlikely(GETSYSCTL_SIMPLE("vm.stats.sys.v_intr", mib, int_number))) { + error("DISABLED: system.dev_intr chart"); + error("DISABLED: vm.stats.sys.v_intr module"); + return 1; + } else { - rrddim_set(st, "interrupts", u_int_data); - rrdset_done(st); - } - } + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + static RRDSET *st = NULL; + static RRDDIM *rd = NULL; - if (likely(do_soft_intr)) { - if (unlikely(GETSYSCTL("vm.stats.sys.v_soft", u_int_data))) { - do_soft_intr = 0; - error("DISABLED: system.dev_intr"); - } else { + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "dev_intr", + NULL, + "interrupts", + NULL, + "Device Interrupts", + "interrupts/s", + 1000, + update_every, + RRDSET_TYPE_LINE + ); + + rd = rrddim_add(st, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else rrdset_next(st); - st = rrdset_find_bytype("system", "soft_intr"); - if (unlikely(!st)) { - st = rrdset_create("system", "soft_intr", NULL, "interrupts", NULL, "Software Interrupts", "interrupts/s", 1100, update_every, RRDSET_TYPE_LINE); + rrddim_set_by_pointer(st, rd, int_number); + rrdset_done(st); + } - rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + return 0; +} - rrddim_set(st, "interrupts", u_int_data); - rrdset_done(st); - } - } +// -------------------------------------------------------------------------------------------------------------------- +// vm.stats.sys.v_soft - // -------------------------------------------------------------------- +int do_vm_stats_sys_v_soft(int update_every, usec_t dt) { + (void)dt; + static int mib[4] = {0, 0, 0, 0}; + u_int soft_intr_number; - if (likely(do_context)) { - if (unlikely(GETSYSCTL("vm.stats.sys.v_swtch", u_int_data))) { - do_context = 0; - error("DISABLED: system.ctxt"); - } else { + if (unlikely(GETSYSCTL_SIMPLE("vm.stats.sys.v_soft", mib, soft_intr_number))) { + error("DISABLED: system.dev_intr chart"); + error("DISABLED: vm.stats.sys.v_soft module"); + return 1; + } else { - 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); + static RRDSET *st = NULL; + static RRDDIM *rd = NULL; - rrddim_set(st, "switches", u_int_data); - rrdset_done(st); + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "soft_intr", + NULL, + "interrupts", + NULL, + "Software Interrupts", + "interrupts/s", + 1100, + update_every, + RRDSET_TYPE_LINE + ); + + rd = rrddim_add(st, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } + else rrdset_next(st); + + rrddim_set_by_pointer(st, rd, soft_intr_number); + rrdset_done(st); } - // -------------------------------------------------------------------- + return 0; +} - if (likely(do_forks)) { - if (unlikely(GETSYSCTL("vm.stats.vm.v_forks", u_int_data))) { - do_forks = 0; - error("DISABLED: system.forks"); - } else { +// -------------------------------------------------------------------------------------------------------------------- +// vm.stats.sys.v_swtch - 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; +int do_vm_stats_sys_v_swtch(int update_every, usec_t dt) { + (void)dt; + static int mib[4] = {0, 0, 0, 0}; + u_int ctxt_number; - rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + if (unlikely(GETSYSCTL_SIMPLE("vm.stats.sys.v_swtch", mib, ctxt_number))) { + error("DISABLED: system.ctxt chart"); + error("DISABLED: vm.stats.sys.v_swtch module"); + return 1; + } else { - rrddim_set(st, "started", u_int_data); - rrdset_done(st); - } - } + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + static RRDSET *st = NULL; + static RRDDIM *rd = NULL; - if (likely(do_disk_io)) { - if (unlikely(GETSYSCTL("kern.devstat.numdevs", numdevs))) { - do_disk_io = 0; - error("DISABLED: disk.io"); - } else { - devstat_data = reallocz(devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs); // there is generation number before devstat structures - if (unlikely(getsysctl("kern.devstat.all", devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs))) { - do_disk_io = 0; - error("DISABLED: disk.io"); - } else { - dstat = devstat_data + sizeof(long); // skip generation number - collected_number total_disk_kbytes_read = 0; - collected_number total_disk_kbytes_write = 0; + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "ctxt", + NULL, + "processes", + NULL, + "CPU Context Switches", + "context switches/s", + 800, + update_every, + RRDSET_TYPE_LINE + ); + + rd = rrddim_add(st, "switches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else rrdset_next(st); - for (i = 0; i < numdevs; i++) { - if (((dstat[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) || ((dstat[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_STORARRAY)) { - sprintf(disk, "%s%d", dstat[i].device_name, dstat[i].unit_number); + rrddim_set_by_pointer(st, rd, ctxt_number); + rrdset_done(st); + } - // -------------------------------------------------------------------- + return 0; +} - st = rrdset_find_bytype(RRD_TYPE_DISK, disk); - if (unlikely(!st)) { - st = rrdset_create(RRD_TYPE_DISK, disk, NULL, disk, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA); +// -------------------------------------------------------------------------------------------------------------------- +// vm.stats.vm.v_forks - rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); +int do_vm_stats_sys_v_forks(int update_every, usec_t dt) { + (void)dt; + static int mib[4] = {0, 0, 0, 0}; + u_int forks_number; - total_disk_kbytes_read += dstat[i].bytes[DEVSTAT_READ]/KILO_FACTOR; - total_disk_kbytes_write += dstat[i].bytes[DEVSTAT_WRITE]/KILO_FACTOR; - prev_dstat.bytes_read = rrddim_set(st, "reads", dstat[i].bytes[DEVSTAT_READ]); - prev_dstat.bytes_write = rrddim_set(st, "writes", dstat[i].bytes[DEVSTAT_WRITE]); - rrdset_done(st); + if (unlikely(GETSYSCTL_SIMPLE("vm.stats.vm.v_forks", mib, forks_number))) { + error("DISABLED: system.forks chart"); + error("DISABLED: vm.stats.sys.v_swtch module"); + return 1; + } else { - // -------------------------------------------------------------------- + // -------------------------------------------------------------------- - st = rrdset_find_bytype("disk_ops", disk); - if (unlikely(!st)) { - st = rrdset_create("disk_ops", disk, NULL, disk, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + static RRDSET *st = NULL; + static RRDDIM *rd = NULL; - rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "forks", + NULL, + "processes", + NULL, + "Started Processes", + "processes/s", + 700, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd = rrddim_add(st, "started", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else rrdset_next(st); - prev_dstat.operations_read = rrddim_set(st, "reads", dstat[i].operations[DEVSTAT_READ]); - prev_dstat.operations_write = rrddim_set(st, "writes", dstat[i].operations[DEVSTAT_WRITE]); - rrdset_done(st); + rrddim_set_by_pointer(st, rd, forks_number); + rrdset_done(st); + } - // -------------------------------------------------------------------- + return 0; +} - st = rrdset_find_bytype("disk_qops", disk); - if (unlikely(!st)) { - st = rrdset_create("disk_qops", disk, NULL, disk, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; +// -------------------------------------------------------------------------------------------------------------------- +// vm.swap_info - rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); +int do_vm_swap_info(int update_every, usec_t dt) { + (void)dt; + static int mib[3] = {0, 0, 0}; + + if (unlikely(getsysctl_mib("vm.swap_info", mib, 2))) { + error("DISABLED: system.swap chart"); + error("DISABLED: vm.swap_info module"); + return 1; + } else { + int i; + struct xswdev xsw; + struct total_xsw { + collected_number bytes_used; + collected_number bytes_total; + } total_xsw = {0, 0}; + + for (i = 0; ; i++) { + size_t size; + + mib[2] = i; + size = sizeof(xsw); + if (unlikely(sysctl(mib, 3, &xsw, &size, NULL, 0) == -1 )) { + if (unlikely(errno != ENOENT)) { + error("FREEBSD: sysctl(%s...) failed: %s", "vm.swap_info", strerror(errno)); + error("DISABLED: system.swap chart"); + error("DISABLED: vm.swap_info module"); + return 1; + } else { + if (unlikely(size != sizeof(xsw))) { + error("FREEBSD: sysctl(%s...) expected %lu, got %lu", "vm.swap_info", (unsigned long)sizeof(xsw), (unsigned long)size); + error("DISABLED: system.swap chart"); + error("DISABLED: vm.swap_info module"); + return 1; + } else break; + } + } + total_xsw.bytes_used += xsw.xsw_used; + total_xsw.bytes_total += xsw.xsw_nblks; + } - rrddim_set(st, "operations", dstat[i].start_count - dstat[i].end_count); - rrdset_done(st); + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + static RRDSET *st = NULL; + static RRDDIM *rd_free = NULL, *rd_used = NULL; - st = rrdset_find_bytype("disk_util", disk); - if (unlikely(!st)) { - st = rrdset_create("disk_util", disk, NULL, disk, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA); - st->isdetail = 1; + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "swap", + NULL, + "swap", + NULL, + "System Swap", + "MB", + 201, + update_every, + RRDSET_TYPE_STACKED + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_free = rrddim_add(st, "free", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); + rd_used = rrddim_add(st, "used", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); + } + else rrdset_next(st); - rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + rrddim_set_by_pointer(st, rd_free, total_xsw.bytes_total - total_xsw.bytes_used); + rrddim_set_by_pointer(st, rd_used, total_xsw.bytes_used); + rrdset_done(st); + } - cur_dstat.busy_time_ms = dstat[i].busy_time.sec * 1000 + dstat[i].busy_time.frac * BINTIME_SCALE; - prev_dstat.busy_time_ms = rrddim_set(st, "utilization", cur_dstat.busy_time_ms); - rrdset_done(st); + return 0; +} - // -------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- +// system.ram - st = rrdset_find_bytype("disk_iotime", disk); - if (unlikely(!st)) { - st = rrdset_create("disk_iotime", disk, NULL, disk, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; +int do_system_ram(int update_every, usec_t dt) { + (void)dt; + static int mib_active_count[4] = {0, 0, 0, 0}, mib_inactive_count[4] = {0, 0, 0, 0}, mib_wire_count[4] = {0, 0, 0, 0}, + mib_cache_count[4] = {0, 0, 0, 0}, mib_vfs_bufspace[2] = {0, 0}, mib_free_count[4] = {0, 0, 0, 0}; + struct vmmeter vmmeter_data; + int vfs_bufspace_count; - rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + if (unlikely(GETSYSCTL_SIMPLE("vm.stats.vm.v_active_count", mib_active_count, vmmeter_data.v_active_count) || + GETSYSCTL_SIMPLE("vm.stats.vm.v_inactive_count", mib_inactive_count, vmmeter_data.v_inactive_count) || + GETSYSCTL_SIMPLE("vm.stats.vm.v_wire_count", mib_wire_count, vmmeter_data.v_wire_count) || +#if __FreeBSD_version < 1200016 + GETSYSCTL_SIMPLE("vm.stats.vm.v_cache_count", mib_cache_count, vmmeter_data.v_cache_count) || +#endif + GETSYSCTL_SIMPLE("vfs.bufspace", mib_vfs_bufspace, vfs_bufspace_count) || + GETSYSCTL_SIMPLE("vm.stats.vm.v_free_count", mib_free_count, vmmeter_data.v_free_count))) { + error("DISABLED: system.ram chart"); + error("DISABLED: System.ram module"); + return 1; + } else { - cur_dstat.duration_read_ms = dstat[i].duration[DEVSTAT_READ].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; - cur_dstat.duration_write_ms = dstat[i].duration[DEVSTAT_WRITE].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; - prev_dstat.duration_read_ms = rrddim_set(st, "reads", cur_dstat.duration_read_ms); - prev_dstat.duration_write_ms = rrddim_set(st, "writes", cur_dstat.duration_write_ms); - rrdset_done(st); + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- - // calculate differential charts - // only if this is not the first time we run + static RRDSET *st = NULL; + static RRDDIM *rd_free = NULL, *rd_active = NULL, *rd_inactive = NULL, + *rd_wired = NULL, *rd_cache = NULL, *rd_buffers = NULL; - if (likely(dt)) { + st = rrdset_find_localhost("system.ram"); + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "ram", + NULL, + "ram", + NULL, + "System RAM", + "MB", + 200, + update_every, + RRDSET_TYPE_STACKED + ); + + rd_free = rrddim_add(st, "free", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); + rd_active = rrddim_add(st, "active", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); + rd_inactive = rrddim_add(st, "inactive", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); + rd_wired = rrddim_add(st, "wired", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); +#if __FreeBSD_version < 1200016 + rd_cache = rrddim_add(st, "cache", NULL, system_pagesize, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); +#endif + rd_buffers = rrddim_add(st, "buffers", NULL, 1, MEGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); + } + else rrdset_next(st); - // -------------------------------------------------------------------- + rrddim_set_by_pointer(st, rd_free, vmmeter_data.v_free_count); + rrddim_set_by_pointer(st, rd_active, vmmeter_data.v_active_count); + rrddim_set_by_pointer(st, rd_inactive, vmmeter_data.v_inactive_count); + rrddim_set_by_pointer(st, rd_wired, vmmeter_data.v_wire_count); +#if __FreeBSD_version < 1200016 + rrddim_set_by_pointer(st, rd_cache, vmmeter_data.v_cache_count); +#endif + rrddim_set_by_pointer(st, rd_buffers, vfs_bufspace_count); + rrdset_done(st); + } - st = rrdset_find_bytype("disk_await", disk); - if (unlikely(!st)) { - st = rrdset_create("disk_await", disk, NULL, disk, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + return 0; +} - rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); +// -------------------------------------------------------------------------------------------------------------------- +// vm.stats.vm.v_swappgs - rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ? - (cur_dstat.duration_read_ms - prev_dstat.duration_read_ms) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0); - rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ? - (cur_dstat.duration_write_ms - prev_dstat.duration_write_ms) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0); - rrdset_done(st); +int do_vm_stats_sys_v_swappgs(int update_every, usec_t dt) { + (void)dt; + static int mib_swappgsin[4] = {0, 0, 0, 0}, mib_swappgsout[4] = {0, 0, 0, 0}; + struct vmmeter vmmeter_data; - // -------------------------------------------------------------------- + if (unlikely(GETSYSCTL_SIMPLE("vm.stats.vm.v_swappgsin", mib_swappgsin, vmmeter_data.v_swappgsin) || + GETSYSCTL_SIMPLE("vm.stats.vm.v_swappgsout", mib_swappgsout, vmmeter_data.v_swappgsout))) { + error("DISABLED: system.swapio chart"); + error("DISABLED: vm.stats.vm.v_swappgs module"); + return 1; + } else { - st = rrdset_find_bytype("disk_avgsz", disk); - if (unlikely(!st)) { - st = rrdset_create("disk_avgsz", disk, NULL, disk, "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, 1, 1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); + static RRDSET *st = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; - rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ? - (dstat[i].bytes[DEVSTAT_READ] - prev_dstat.bytes_read) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0); - rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ? - (dstat[i].bytes[DEVSTAT_WRITE] - prev_dstat.bytes_write) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0); - rrdset_done(st); + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "swapio", + NULL, + "swap", + NULL, + "Swap I/O", + "kilobytes/s", + 250, + update_every, + RRDSET_TYPE_AREA + ); + + rd_in = rrddim_add(st, "in", NULL, system_pagesize, KILO_FACTOR, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st, "out", NULL, -system_pagesize, KILO_FACTOR, RRD_ALGORITHM_INCREMENTAL); + } + else rrdset_next(st); - // -------------------------------------------------------------------- + rrddim_set_by_pointer(st, rd_in, vmmeter_data.v_swappgsin); + rrddim_set_by_pointer(st, rd_out, vmmeter_data.v_swappgsout); + rrdset_done(st); + } - st = rrdset_find_bytype("disk_svctm", disk); - if (unlikely(!st)) { - st = rrdset_create("disk_svctm", disk, NULL, disk, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + return 0; +} - rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); +// -------------------------------------------------------------------------------------------------------------------- +// vm.stats.vm.v_pgfaults - rrddim_set(st, "svctm", ((dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) + (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write)) ? - (cur_dstat.busy_time_ms - prev_dstat.busy_time_ms) / ((dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) + (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write)) : 0); - rrdset_done(st); - } - } - } +int do_vm_stats_sys_v_pgfaults(int update_every, usec_t dt) { + (void)dt; + static int mib_vm_faults[4] = {0, 0, 0, 0}, mib_io_faults[4] = {0, 0, 0, 0}, mib_cow_faults[4] = {0, 0, 0, 0}, + mib_cow_optim[4] = {0, 0, 0, 0}, mib_intrans[4] = {0, 0, 0, 0}; + struct vmmeter vmmeter_data; - // -------------------------------------------------------------------- + if (unlikely(GETSYSCTL_SIMPLE("vm.stats.vm.v_vm_faults", mib_vm_faults, vmmeter_data.v_vm_faults) || + GETSYSCTL_SIMPLE("vm.stats.vm.v_io_faults", mib_io_faults, vmmeter_data.v_io_faults) || + GETSYSCTL_SIMPLE("vm.stats.vm.v_cow_faults", mib_cow_faults, vmmeter_data.v_cow_faults) || + GETSYSCTL_SIMPLE("vm.stats.vm.v_cow_optim", mib_cow_optim, vmmeter_data.v_cow_optim) || + GETSYSCTL_SIMPLE("vm.stats.vm.v_intrans", mib_intrans, vmmeter_data.v_intrans))) { + error("DISABLED: mem.pgfaults chart"); + error("DISABLED: vm.stats.vm.v_pgfaults module"); + return 1; + } else { - st = rrdset_find_bytype("system", "io"); - if (unlikely(!st)) { - st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "in", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "out", NULL, -1, 1, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + // -------------------------------------------------------------------- - rrddim_set(st, "in", total_disk_kbytes_read); - rrddim_set(st, "out", total_disk_kbytes_write); - rrdset_done(st); - } + static RRDSET *st = NULL; + static RRDDIM *rd_memory = NULL, *rd_io_requiring = NULL, *rd_cow = NULL, + *rd_cow_optimized = NULL, *rd_in_transit = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("mem", + "pgfaults", + NULL, + "system", + NULL, + "Memory Page Faults", + "page faults/s", + 500, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_memory = rrddim_add(st, "memory", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_io_requiring = rrddim_add(st, "io_requiring", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_cow = rrddim_add(st, "cow", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_cow_optimized = rrddim_add(st, "cow_optimized", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_transit = rrddim_add(st, "in_transit", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } + else rrdset_next(st); + + rrddim_set_by_pointer(st, rd_memory, vmmeter_data.v_vm_faults); + rrddim_set_by_pointer(st, rd_io_requiring, vmmeter_data.v_io_faults); + rrddim_set_by_pointer(st, rd_cow, vmmeter_data.v_cow_faults); + rrddim_set_by_pointer(st, rd_cow_optimized, vmmeter_data.v_cow_optim); + rrddim_set_by_pointer(st, rd_in_transit, vmmeter_data.v_intrans); + rrdset_done(st); } - // -------------------------------------------------------------------- + return 0; +} +// -------------------------------------------------------------------------------------------------------------------- +// kern.ipc.sem - if (likely(do_swap)) { - mibsize = sizeof mib / sizeof mib[0]; - if (unlikely(sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)) { - error("FREEBSD: sysctl(%s...) failed: %s", "vm.swap_info", strerror(errno)); - do_swap = 0; - error("DISABLED: system.swap"); - } else { - for (i = 0; ; i++) { - mib[mibsize] = i; - size = sizeof(xsw); - if (unlikely(sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1 )) { - if (unlikely(errno != ENOENT)) { - error("FREEBSD: sysctl(%s...) failed: %s", "vm.swap_info", strerror(errno)); - do_swap = 0; - error("DISABLED: system.swap"); - } else { - if (unlikely(size != sizeof(xsw))) { - error("FREEBSD: sysctl(%s...) expected %lu, got %lu", "vm.swap_info", (unsigned long)sizeof(xsw), (unsigned long)size); - do_swap = 0; - error("DISABLED: system.swap"); - } else break; - } - } - total_xsw.bytes_used += xsw.xsw_used; - total_xsw.bytes_total += xsw.xsw_nblks; - } +int do_kern_ipc_sem(int update_every, usec_t dt) { + (void)dt; + static int mib_semmni[3] = {0, 0, 0}, mib_sema[3] = {0, 0, 0}; + struct ipc_sem { + int semmni; + collected_number sets; + collected_number semaphores; + } ipc_sem = {0, 0, 0}; - if (likely(do_swap)) { - st = rrdset_find("system.swap"); - if (unlikely(!st)) { - st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED); - st->isdetail = 1; + if (unlikely(GETSYSCTL_SIMPLE("kern.ipc.semmni", mib_semmni, ipc_sem.semmni))) { + error("DISABLED: system.ipc_semaphores chart"); + error("DISABLED: system.ipc_semaphore_arrays chart"); + error("DISABLED: kern.ipc.sem module"); + return 1; + } else { + static struct semid_kernel *ipc_sem_data = NULL; + + ipc_sem_data = reallocz(ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni); + if (unlikely(GETSYSCTL_WSIZE("kern.ipc.sema", mib_sema, ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni))) { + error("DISABLED: system.ipc_semaphores chart"); + error("DISABLED: system.ipc_semaphore_arrays chart"); + error("DISABLED: kern.ipc.sem module"); + return 1; + } else { + int i; - rrddim_add(st, "free", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); - rrddim_add(st, "used", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + for (i = 0; i < ipc_sem.semmni; i++) { + if (unlikely(ipc_sem_data[i].u.sem_perm.mode & SEM_ALLOC)) { + ipc_sem.sets += 1; + ipc_sem.semaphores += ipc_sem_data[i].u.sem_nsems; } - else rrdset_next(st); - - rrddim_set(st, "used", total_xsw.bytes_used); - rrddim_set(st, "free", total_xsw.bytes_total - total_xsw.bytes_used); - rrdset_done(st); } - } - } - // -------------------------------------------------------------------- + // -------------------------------------------------------------------- - if (likely(do_ram)) { - if (unlikely(GETSYSCTL("vm.stats.vm.v_active_count", vmmeter_data.v_active_count) || - GETSYSCTL("vm.stats.vm.v_inactive_count", vmmeter_data.v_inactive_count) || - GETSYSCTL("vm.stats.vm.v_wire_count", vmmeter_data.v_wire_count) || - GETSYSCTL("vm.stats.vm.v_cache_count", vmmeter_data.v_cache_count) || - GETSYSCTL("vfs.bufspace", vfs_bufspace_count) || - GETSYSCTL("vm.stats.vm.v_free_count", vmmeter_data.v_free_count))) { - do_ram = 0; - error("DISABLED: system.ram"); - } else { - st = rrdset_find("system.ram"); - if (unlikely(!st)) { - st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED); - - rrddim_add(st, "active", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); - rrddim_add(st, "inactive", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); - rrddim_add(st, "wired", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); - rrddim_add(st, "cache", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); - rrddim_add(st, "buffers", NULL, 1, MEGA_FACTOR, RRDDIM_ABSOLUTE); - rrddim_add(st, "free", NULL, system_pagesize, MEGA_FACTOR, RRDDIM_ABSOLUTE); + static RRDSET *st_semaphores = NULL, *st_semaphore_arrays = NULL; + static RRDDIM *rd_semaphores = NULL, *rd_semaphore_arrays = NULL; + + if (unlikely(!st_semaphores)) { + st_semaphores = rrdset_create_localhost("system", + "ipc_semaphores", + NULL, + "ipc semaphores", + NULL, + "IPC Semaphores", + "semaphores", + 1000, + update_every, + RRDSET_TYPE_AREA + ); + + rd_semaphores = rrddim_add(st_semaphores, "semaphores", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); - - rrddim_set(st, "active", vmmeter_data.v_active_count); - rrddim_set(st, "inactive", vmmeter_data.v_inactive_count); - rrddim_set(st, "wired", vmmeter_data.v_wire_count); - rrddim_set(st, "cache", vmmeter_data.v_cache_count); - rrddim_set(st, "buffers", vfs_bufspace_count); - rrddim_set(st, "free", vmmeter_data.v_free_count); - rrdset_done(st); - } - } + else rrdset_next(st_semaphores); - // -------------------------------------------------------------------- + rrddim_set_by_pointer(st_semaphores, rd_semaphores, ipc_sem.semaphores); + rrdset_done(st_semaphores); - if (likely(do_swapio)) { - if (unlikely(GETSYSCTL("vm.stats.vm.v_swappgsin", vmmeter_data.v_swappgsin) || GETSYSCTL("vm.stats.vm.v_swappgsout", vmmeter_data.v_swappgsout))) { - do_swapio = 0; - error("DISABLED: system.swapio"); - } else { - st = rrdset_find("system.swapio"); - if (unlikely(!st)) { - st = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA); + // -------------------------------------------------------------------- - rrddim_add(st, "in", NULL, system_pagesize, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRDDIM_INCREMENTAL); + if (unlikely(!st_semaphore_arrays)) { + st_semaphore_arrays = rrdset_create_localhost("system", + "ipc_semaphore_arrays", + NULL, + "ipc semaphores", + NULL, + "IPC Semaphore Arrays", + "arrays", + 1000, + update_every, + RRDSET_TYPE_AREA + ); + + rd_semaphore_arrays = rrddim_add(st_semaphore_arrays, "arrays", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); + else rrdset_next(st_semaphore_arrays); - rrddim_set(st, "in", vmmeter_data.v_swappgsin); - rrddim_set(st, "out", vmmeter_data.v_swappgsout); - rrdset_done(st); + rrddim_set_by_pointer(st_semaphore_arrays, rd_semaphore_arrays, ipc_sem.sets); + rrdset_done(st_semaphore_arrays); } } - // -------------------------------------------------------------------- - - if (likely(do_pgfaults)) { - if (unlikely(GETSYSCTL("vm.stats.vm.v_vm_faults", vmmeter_data.v_vm_faults) || - GETSYSCTL("vm.stats.vm.v_io_faults", vmmeter_data.v_io_faults) || - GETSYSCTL("vm.stats.vm.v_cow_faults", vmmeter_data.v_cow_faults) || - GETSYSCTL("vm.stats.vm.v_cow_optim", vmmeter_data.v_cow_optim) || - GETSYSCTL("vm.stats.vm.v_intrans", vmmeter_data.v_intrans))) { - do_pgfaults = 0; - error("DISABLED: mem.pgfaults"); - } else { - st = rrdset_find("mem.pgfaults"); - if (unlikely(!st)) { - st = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; - - rrddim_add(st, "memory", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "io_requiring", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "cow", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "cow_optimized", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "in_transit", NULL, 1, 1, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + return 0; +} - rrddim_set(st, "memory", vmmeter_data.v_vm_faults); - rrddim_set(st, "io_requiring", vmmeter_data.v_io_faults); - rrddim_set(st, "cow", vmmeter_data.v_cow_faults); - rrddim_set(st, "cow_optimized", vmmeter_data.v_cow_optim); - rrddim_set(st, "in_transit", vmmeter_data.v_intrans); - rrdset_done(st); - } - } +// -------------------------------------------------------------------------------------------------------------------- +// kern.ipc.shm - // -------------------------------------------------------------------- +int do_kern_ipc_shm(int update_every, usec_t dt) { + (void)dt; + static int mib_shmmni[3] = {0, 0, 0}, mib_shmsegs[3] = {0, 0, 0}; + struct ipc_shm { + u_long shmmni; + collected_number segs; + collected_number segsize; + } ipc_shm = {0, 0, 0}; - if (likely(do_ipc_semaphores)) { - if (unlikely(GETSYSCTL("kern.ipc.semmni", ipc_sem.semmni))) { - do_ipc_semaphores = 0; - error("DISABLED: system.ipc_semaphores"); - error("DISABLED: system.ipc_semaphore_arrays"); + if (unlikely(GETSYSCTL_SIMPLE("kern.ipc.shmmni", mib_shmmni, ipc_shm.shmmni))) { + error("DISABLED: system.ipc_shared_mem_segs chart"); + error("DISABLED: system.ipc_shared_mem_size chart"); + error("DISABLED: kern.ipc.shmmodule"); + return 1; + } else { + static struct shmid_kernel *ipc_shm_data = NULL; + + ipc_shm_data = reallocz(ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni); + if (unlikely( + GETSYSCTL_WSIZE("kern.ipc.shmsegs", mib_shmsegs, ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni))) { + error("DISABLED: system.ipc_shared_mem_segs chart"); + error("DISABLED: system.ipc_shared_mem_size chart"); + error("DISABLED: kern.ipc.shmmodule"); + return 1; } else { - ipc_sem_data = reallocz(ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni); - if (unlikely(getsysctl("kern.ipc.sema", ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni))) { - do_ipc_semaphores = 0; - error("DISABLED: system.ipc_semaphores"); - error("DISABLED: system.ipc_semaphore_arrays"); - } else { - for (i = 0; i < ipc_sem.semmni; i++) { - if (unlikely(ipc_sem_data[i].u.sem_perm.mode & SEM_ALLOC)) { - ipc_sem.sets += 1; - ipc_sem.semaphores += ipc_sem_data[i].u.sem_nsems; - } - } - - // -------------------------------------------------------------------- + unsigned long i; - st = rrdset_find("system.ipc_semaphores"); - if (unlikely(!st)) { - st = rrdset_create("system", "ipc_semaphores", NULL, "ipc semaphores", NULL, "IPC Semaphores", "semaphores", 1000, rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "semaphores", NULL, 1, 1, RRDDIM_ABSOLUTE); + for (i = 0; i < ipc_shm.shmmni; i++) { + if (unlikely(ipc_shm_data[i].u.shm_perm.mode & 0x0800)) { + ipc_shm.segs += 1; + ipc_shm.segsize += ipc_shm_data[i].u.shm_segsz; } - else rrdset_next(st); + } - rrddim_set(st, "semaphores", ipc_sem.semaphores); - rrdset_done(st); + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + static RRDSET *st_segs = NULL, *st_size = NULL; + static RRDDIM *rd_segments = NULL, *rd_allocated = NULL; + + if (unlikely(!st_segs)) { + st_segs = rrdset_create_localhost("system", + "ipc_shared_mem_segs", + NULL, + "ipc shared memory", + NULL, + "IPC Shared Memory Segments", + "segments", + 1000, + update_every, + RRDSET_TYPE_AREA + ); + + rd_segments = rrddim_add(st_segs, "segments", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + else rrdset_next(st_segs); - st = rrdset_find("system.ipc_semaphore_arrays"); - if (unlikely(!st)) { - st = rrdset_create("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL, "IPC Semaphore Arrays", "arrays", 1000, rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "arrays", NULL, 1, 1, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); + rrddim_set_by_pointer(st_segs, rd_segments, ipc_shm.segs); + rrdset_done(st_segs); - rrddim_set(st, "arrays", ipc_sem.sets); - rrdset_done(st); + // -------------------------------------------------------------------- + + if (unlikely(!st_size)) { + st_size = rrdset_create_localhost("system", + "ipc_shared_mem_size", + NULL, + "ipc shared memory", + NULL, + "IPC Shared Memory Segments Size", + "kilobytes", + 1000, + update_every, + RRDSET_TYPE_AREA + ); + + rd_allocated = rrddim_add(st_size, "allocated", NULL, 1, KILO_FACTOR, RRD_ALGORITHM_ABSOLUTE); } + else rrdset_next(st_size); + + rrddim_set_by_pointer(st_size, rd_allocated, ipc_shm.segsize); + rrdset_done(st_size); } } - // -------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// kern.ipc.msq + +int do_kern_ipc_msq(int update_every, usec_t dt) { + (void)dt; + static int mib_msgmni[3] = {0, 0, 0}, mib_msqids[3] = {0, 0, 0}; + struct ipc_msq { + int msgmni; + collected_number queues; + collected_number messages; + collected_number usedsize; + collected_number allocsize; + } ipc_msq = {0, 0, 0, 0, 0}; - if (likely(do_ipc_shared_mem)) { - if (unlikely(GETSYSCTL("kern.ipc.shmmni", ipc_shm.shmmni))) { - do_ipc_shared_mem = 0; - error("DISABLED: system.ipc_shared_mem_segs"); - error("DISABLED: system.ipc_shared_mem_size"); + if (unlikely(GETSYSCTL_SIMPLE("kern.ipc.msgmni", mib_msgmni, ipc_msq.msgmni))) { + error("DISABLED: system.ipc_msq_queues chart"); + error("DISABLED: system.ipc_msq_messages chart"); + error("DISABLED: system.ipc_msq_size chart"); + error("DISABLED: kern.ipc.msg module"); + return 1; + } else { + static struct msqid_kernel *ipc_msq_data = NULL; + + ipc_msq_data = reallocz(ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni); + if (unlikely( + GETSYSCTL_WSIZE("kern.ipc.msqids", mib_msqids, ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni))) { + error("DISABLED: system.ipc_msq_queues chart"); + error("DISABLED: system.ipc_msq_messages chart"); + error("DISABLED: system.ipc_msq_size chart"); + error("DISABLED: kern.ipc.msg module"); + return 1; } else { - ipc_shm_data = reallocz(ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni); - if (unlikely(getsysctl("kern.ipc.shmsegs", ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni))) { - do_ipc_shared_mem = 0; - error("DISABLED: system.ipc_shared_mem_segs"); - error("DISABLED: system.ipc_shared_mem_size"); - } else { - for (i = 0; i < ipc_shm.shmmni; i++) { - if (unlikely(ipc_shm_data[i].u.shm_perm.mode & 0x0800)) { - ipc_shm.segs += 1; - ipc_shm.segsize += ipc_shm_data[i].u.shm_segsz; - } + int i; + + for (i = 0; i < ipc_msq.msgmni; i++) { + if (unlikely(ipc_msq_data[i].u.msg_qbytes != 0)) { + ipc_msq.queues += 1; + ipc_msq.messages += ipc_msq_data[i].u.msg_qnum; + ipc_msq.usedsize += ipc_msq_data[i].u.msg_cbytes; + ipc_msq.allocsize += ipc_msq_data[i].u.msg_qbytes; } + } - // -------------------------------------------------------------------- + // -------------------------------------------------------------------- - st = rrdset_find("system.ipc_shared_mem_segs"); - if (unlikely(!st)) { - st = rrdset_create("system", "ipc_shared_mem_segs", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments", "segments", 1000, rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "segments", NULL, 1, 1, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); + static RRDSET *st_queues = NULL, *st_messages = NULL, *st_size = NULL; + static RRDDIM *rd_queues = NULL, *rd_messages = NULL, *rd_allocated = NULL, *rd_used = NULL; + + if (unlikely(!st_queues)) { + st_queues = rrdset_create_localhost("system", + "ipc_msq_queues", + NULL, + "ipc message queues", + NULL, + "Number of IPC Message Queues", + "queues", + 990, + update_every, + RRDSET_TYPE_AREA + ); + + rd_queues = rrddim_add(st_queues, "queues", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + else rrdset_next(st_queues); - rrddim_set(st, "segments", ipc_shm.segs); - rrdset_done(st); + rrddim_set_by_pointer(st_queues, rd_queues, ipc_msq.queues); + rrdset_done(st_queues); - // -------------------------------------------------------------------- + // -------------------------------------------------------------------- - st = rrdset_find("system.ipc_shared_mem_size"); - if (unlikely(!st)) { - st = rrdset_create("system", "ipc_shared_mem_size", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments Size", "kilobytes", 1000, rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "allocated", NULL, 1, 1024, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); + if (unlikely(!st_messages)) { + st_messages = rrdset_create_localhost("system", + "ipc_msq_messages", + NULL, + "ipc message queues", + NULL, + "Number of Messages in IPC Message Queues", + "messages", + 1000, + update_every, + RRDSET_TYPE_AREA + ); + + rd_messages = rrddim_add(st_messages, "messages", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + else rrdset_next(st_messages); - rrddim_set(st, "allocated", ipc_shm.segsize); - rrdset_done(st); + rrddim_set_by_pointer(st_messages, rd_messages, ipc_msq.messages); + rrdset_done(st_messages); + + // -------------------------------------------------------------------- + + if (unlikely(!st_size)) { + st_size = rrdset_create_localhost("system", + "ipc_msq_size", + NULL, + "ipc message queues", + NULL, + "Size of IPC Message Queues", + "bytes", + 1100, + update_every, + RRDSET_TYPE_LINE + ); + + rd_allocated = rrddim_add(st_size, "allocated", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_used = rrddim_add(st_size, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } + else rrdset_next(st_size); + + rrddim_set_by_pointer(st_size, rd_allocated, ipc_msq.allocsize); + rrddim_set_by_pointer(st_size, rd_used, ipc_msq.usedsize); + rrdset_done(st_size); } } - // -------------------------------------------------------------------- - - if (likely(do_ipc_msg_queues)) { - if (unlikely(GETSYSCTL("kern.ipc.msgmni", ipc_msq.msgmni))) { - do_ipc_msg_queues = 0; - error("DISABLED: system.ipc_msq_queues"); - error("DISABLED: system.ipc_msq_messages"); - error("DISABLED: system.ipc_msq_size"); - } else { - ipc_msq_data = reallocz(ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni); - if (unlikely(getsysctl("kern.ipc.msqids", ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni))) { - do_ipc_msg_queues = 0; - error("DISABLED: system.ipc_msq_queues"); - error("DISABLED: system.ipc_msq_messages"); - error("DISABLED: system.ipc_msq_size"); - } else { - for (i = 0; i < ipc_msq.msgmni; i++) { - if (unlikely(ipc_msq_data[i].u.msg_qbytes != 0)) { - ipc_msq.queues += 1; - ipc_msq.messages += ipc_msq_data[i].u.msg_qnum; - ipc_msq.usedsize += ipc_msq_data[i].u.msg_cbytes; - ipc_msq.allocsize += ipc_msq_data[i].u.msg_qbytes; - } - } + return 0; +} - // -------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- +// uptime - st = rrdset_find("system.ipc_msq_queues"); - if (unlikely(!st)) { - st = rrdset_create("system", "ipc_msq_queues", NULL, "ipc message queues", NULL, "Number of IPC Message Queues", "queues", 990, rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "queues", NULL, 1, 1, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); +int do_uptime(int update_every, usec_t dt) { + (void)dt; + struct timespec up_time; - rrddim_set(st, "queues", ipc_msq.queues); - rrdset_done(st); + clock_gettime(CLOCK_UPTIME, &up_time); - // -------------------------------------------------------------------- + // -------------------------------------------------------------------- - st = rrdset_find("system.ipc_msq_messages"); - if (unlikely(!st)) { - st = rrdset_create("system", "ipc_msq_messages", NULL, "ipc message queues", NULL, "Number of Messages in IPC Message Queues", "messages", 1000, rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "messages", NULL, 1, 1, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); + static RRDSET *st = NULL; + static RRDDIM *rd = NULL; + + if(unlikely(!st)) { + st = rrdset_create_localhost("system", + "uptime", + NULL, + "uptime", + NULL, + "System Uptime", + "seconds", + 1000, + update_every, + RRDSET_TYPE_LINE + ); + + rd = rrddim_add(st, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + else rrdset_next(st); - rrddim_set(st, "messages", ipc_msq.messages); - rrdset_done(st); + rrddim_set_by_pointer(st, rd, up_time.tv_sec); + rrdset_done(st); - // -------------------------------------------------------------------- + return 0; +} - st = rrdset_find("system.ipc_msq_size"); - if (unlikely(!st)) { - st = rrdset_create("system", "ipc_msq_size", NULL, "ipc message queues", NULL, "Size of IPC Message Queues", "bytes", 1100, rrd_update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "allocated", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(st, "used", NULL, 1, 1, RRDDIM_ABSOLUTE); - } - else rrdset_next(st); +// -------------------------------------------------------------------------------------------------------------------- +// net.isr - rrddim_set(st, "allocated", ipc_msq.allocsize); - rrddim_set(st, "used", ipc_msq.usedsize); - rrdset_done(st); +int do_net_isr(int update_every, usec_t dt) { + (void)dt; + static int do_netisr = -1, do_netisr_per_core = -1; - } - } + if (unlikely(do_netisr == -1)) { + do_netisr = config_get_boolean("plugin:freebsd:net.isr", "netisr", 1); + do_netisr_per_core = config_get_boolean("plugin:freebsd:net.isr", "netisr per core", 1); } - // -------------------------------------------------------------------- + static int mib_workstream[3] = {0, 0, 0}, mib_work[3] = {0, 0, 0}; + int common_error = 0; + size_t netisr_workstream_size = sizeof(mib_workstream), netisr_work_size = sizeof(mib_work); + unsigned long num_netisr_workstreams = 0, num_netisr_works = 0; + static struct sysctl_netisr_workstream *netisr_workstream = NULL; + static struct sysctl_netisr_work *netisr_work = NULL; + static struct netisr_stats { + collected_number dispatched; + collected_number hybrid_dispatched; + collected_number qdrops; + collected_number queued; + } *netisr_stats = NULL; if (likely(do_netisr || do_netisr_per_core)) { - if (unlikely(GETSYSCTL("kern.smp.cpus", ncpus))) { - common_error = 1; - } else if (unlikely(ncpus > 9999)) { - error("FREEBSD: There are more than 4 digits in cpu cores number"); - common_error = 1; - } else if (unlikely(sysctlbyname("net.isr.workstream", NULL, &netisr_workstream_size, NULL, 0) == -1)) { - error("FREEBSD: sysctl(net.isr.workstream...) failed: %s", strerror(errno)); + if (unlikely(GETSYSCTL_SIZE("net.isr.workstream", mib_workstream, netisr_workstream_size))) { common_error = 1; - } else if (unlikely(sysctlbyname("net.isr.work", NULL, &netisr_work_size, NULL, 0) == -1)) { - error("FREEBSD: sysctl(net.isr.work...) failed: %s", strerror(errno)); + } else if (unlikely(GETSYSCTL_SIZE("net.isr.work", mib_work, netisr_work_size))) { common_error = 1; } else { num_netisr_workstreams = netisr_workstream_size / sizeof(struct sysctl_netisr_workstream); netisr_workstream = reallocz(netisr_workstream, num_netisr_workstreams * sizeof(struct sysctl_netisr_workstream)); - if (unlikely(getsysctl("net.isr.workstream", netisr_workstream, num_netisr_workstreams * sizeof(struct sysctl_netisr_workstream)))){ + if (unlikely(GETSYSCTL_WSIZE("net.isr.workstream", mib_workstream, netisr_workstream, + num_netisr_workstreams * sizeof(struct sysctl_netisr_workstream)))){ common_error = 1; } else { num_netisr_works = netisr_work_size / sizeof(struct sysctl_netisr_work); netisr_work = reallocz(netisr_work, num_netisr_works * sizeof(struct sysctl_netisr_work)); - if (unlikely(getsysctl("net.isr.work", netisr_work, num_netisr_works * sizeof(struct sysctl_netisr_work)))){ + if (unlikely(GETSYSCTL_WSIZE("net.isr.work", mib_work, netisr_work, + num_netisr_works * sizeof(struct sysctl_netisr_work)))){ common_error = 1; } } } if (unlikely(common_error)) { do_netisr = 0; - error("DISABLED: system.softnet_stat"); + error("DISABLED: system.softnet_stat chart"); do_netisr_per_core = 0; - error("DISABLED: system.cpuX_softnet_stat"); + error("DISABLED: system.cpuX_softnet_stat chart"); common_error = 0; + error("DISABLED: net.isr module"); + return 1; } else { - netisr_stats = reallocz(netisr_stats, (ncpus + 1) * sizeof(struct netisr_stats)); - bzero(netisr_stats, (ncpus + 1) * sizeof(struct netisr_stats)); + unsigned long i, n; + int j; + + netisr_stats = reallocz(netisr_stats, (number_of_cpus + 1) * sizeof(struct netisr_stats)); + memset(netisr_stats, 0, (number_of_cpus + 1) * sizeof(struct netisr_stats)); for (i = 0; i < num_netisr_workstreams; i++) { for (n = 0; n < num_netisr_works; n++) { if (netisr_workstream[i].snws_wsid == netisr_work[n].snw_wsid) { @@ -1121,467 +1314,584 @@ int do_freebsd_sysctl(int update_every, usec_t dt) { } } } - for (i = 0; i < ncpus; i++) { - netisr_stats[ncpus].dispatched += netisr_stats[i].dispatched; - netisr_stats[ncpus].hybrid_dispatched += netisr_stats[i].hybrid_dispatched; - netisr_stats[ncpus].qdrops += netisr_stats[i].qdrops; - netisr_stats[ncpus].queued += netisr_stats[i].queued; + for (j = 0; j < number_of_cpus; j++) { + netisr_stats[number_of_cpus].dispatched += netisr_stats[j].dispatched; + netisr_stats[number_of_cpus].hybrid_dispatched += netisr_stats[j].hybrid_dispatched; + netisr_stats[number_of_cpus].qdrops += netisr_stats[j].qdrops; + netisr_stats[number_of_cpus].queued += netisr_stats[j].queued; } } + } else { + error("DISABLED: net.isr module"); + return 1; } // -------------------------------------------------------------------- if (likely(do_netisr)) { - st = rrdset_find_bytype("system", "softnet_stat"); + static RRDSET *st = NULL; + static RRDDIM *rd_dispatched = NULL, *rd_hybrid_dispatched = NULL, *rd_qdrops = NULL, *rd_queued = NULL; + if (unlikely(!st)) { - st = rrdset_create("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat", "events/s", 955, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "hybrid_dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "qdrops", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "queued", NULL, 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("system", + "softnet_stat", + NULL, + "softnet_stat", + NULL, + "System softnet_stat", + "events/s", + 955, + update_every, + RRDSET_TYPE_LINE + ); + + rd_dispatched = rrddim_add(st, "dispatched", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_hybrid_dispatched = rrddim_add(st, "hybrid_dispatched", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_qdrops = rrddim_add(st, "qdrops", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_queued = rrddim_add(st, "queued", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "dispatched", netisr_stats[ncpus].dispatched); - rrddim_set(st, "hybrid_dispatched", netisr_stats[ncpus].hybrid_dispatched); - rrddim_set(st, "qdrops", netisr_stats[ncpus].qdrops); - rrddim_set(st, "queued", netisr_stats[ncpus].queued); + rrddim_set_by_pointer(st, rd_dispatched, netisr_stats[number_of_cpus].dispatched); + rrddim_set_by_pointer(st, rd_hybrid_dispatched, netisr_stats[number_of_cpus].hybrid_dispatched); + rrddim_set_by_pointer(st, rd_qdrops, netisr_stats[number_of_cpus].qdrops); + rrddim_set_by_pointer(st, rd_queued, netisr_stats[number_of_cpus].queued); rrdset_done(st); } // -------------------------------------------------------------------- if (likely(do_netisr_per_core)) { - for (i = 0; i < ncpus ;i++) { - snprintfz(netstat_cpuid, 21, "cpu%d_softnet_stat", i); + static struct softnet_chart { + char netisr_cpuid[MAX_INT_DIGITS + 17]; + RRDSET *st; + RRDDIM *rd_dispatched; + RRDDIM *rd_hybrid_dispatched; + RRDDIM *rd_qdrops; + RRDDIM *rd_queued; + } *all_softnet_charts = NULL; + static int old_number_of_cpus = 0; + int i; + + if(unlikely(number_of_cpus > old_number_of_cpus)) { + all_softnet_charts = reallocz(all_softnet_charts, sizeof(struct softnet_chart) * number_of_cpus); + memset(&all_softnet_charts[old_number_of_cpus], 0, sizeof(struct softnet_chart) * (number_of_cpus - old_number_of_cpus)); + old_number_of_cpus = number_of_cpus; + } - st = rrdset_find_bytype("cpu", netstat_cpuid); - if (unlikely(!st)) { - st = rrdset_create("cpu", netstat_cpuid, NULL, "softnet_stat", NULL, "Per CPU netisr statistics", "events/s", 1101 + i, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "hybrid_dispatched", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "qdrops", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "queued", NULL, 1, 1, RRDDIM_INCREMENTAL); + for (i = 0; i < number_of_cpus ;i++) { + snprintfz(all_softnet_charts[i].netisr_cpuid, MAX_INT_DIGITS + 17, "cpu%d_softnet_stat", i); + + if (unlikely(!all_softnet_charts[i].st)) { + all_softnet_charts[i].st = rrdset_create_localhost("cpu", + all_softnet_charts[i].netisr_cpuid, + NULL, + "softnet_stat", + NULL, + "Per CPU netisr statistics", + "events/s", + 1101 + i, + update_every, + RRDSET_TYPE_LINE + ); + + all_softnet_charts[i].rd_dispatched = rrddim_add(all_softnet_charts[i].st, "dispatched", + NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + all_softnet_charts[i].rd_hybrid_dispatched = rrddim_add(all_softnet_charts[i].st, "hybrid_dispatched", + NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + all_softnet_charts[i].rd_qdrops = rrddim_add(all_softnet_charts[i].st, "qdrops", + NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + all_softnet_charts[i].rd_queued = rrddim_add(all_softnet_charts[i].st, "queued", + NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); - - rrddim_set(st, "dispatched", netisr_stats[i].dispatched); - rrddim_set(st, "hybrid_dispatched", netisr_stats[i].hybrid_dispatched); - rrddim_set(st, "qdrops", netisr_stats[i].qdrops); - rrddim_set(st, "queued", netisr_stats[i].queued); - rrdset_done(st); + else rrdset_next(all_softnet_charts[i].st); + + rrddim_set_by_pointer(all_softnet_charts[i].st, all_softnet_charts[i].rd_dispatched, + netisr_stats[i].dispatched); + rrddim_set_by_pointer(all_softnet_charts[i].st, all_softnet_charts[i].rd_hybrid_dispatched, + netisr_stats[i].hybrid_dispatched); + rrddim_set_by_pointer(all_softnet_charts[i].st, all_softnet_charts[i].rd_qdrops, + netisr_stats[i].qdrops); + rrddim_set_by_pointer(all_softnet_charts[i].st, all_softnet_charts[i].rd_queued, + netisr_stats[i].queued); + rrdset_done(all_softnet_charts[i].st); } } - // -------------------------------------------------------------------- - - if (likely(do_bandwidth)) { - if (unlikely(getifaddrs(&ifap))) { - error("FREEBSD: getifaddrs()"); - do_bandwidth = 0; - error("DISABLED: system.ipv4"); - } else { - iftot.ift_ibytes = iftot.ift_obytes = 0; - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr->sa_family != AF_INET) - continue; - iftot.ift_ibytes += IFA_DATA(ibytes); - iftot.ift_obytes += IFA_DATA(obytes); - } - - st = rrdset_find("system.ipv4"); - if (unlikely(!st)) { - st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA); - - rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set(st, "InOctets", iftot.ift_ibytes); - rrddim_set(st, "OutOctets", iftot.ift_obytes); - rrdset_done(st); - - // -------------------------------------------------------------------- - - iftot.ift_ibytes = iftot.ift_obytes = 0; - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - iftot.ift_ibytes += IFA_DATA(ibytes); - iftot.ift_obytes += IFA_DATA(obytes); - } - - st = rrdset_find("system.ipv6"); - if (unlikely(!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", iftot.ift_obytes); - rrddim_set(st, "received", iftot.ift_ibytes); - rrdset_done(st); - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr->sa_family != AF_LINK) - continue; - - // -------------------------------------------------------------------- - - st = rrdset_find_bytype("net", ifa->ifa_name); - if (unlikely(!st)) { - st = rrdset_create("net", ifa->ifa_name, NULL, ifa->ifa_name, "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", IFA_DATA(ibytes)); - rrddim_set(st, "sent", IFA_DATA(obytes)); - rrdset_done(st); - - // -------------------------------------------------------------------- - - st = rrdset_find_bytype("net_packets", ifa->ifa_name); - if (unlikely(!st)) { - st = rrdset_create("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "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_received", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "multicast_sent", NULL, -1, 1, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); - - rrddim_set(st, "received", IFA_DATA(ipackets)); - rrddim_set(st, "sent", IFA_DATA(opackets)); - rrddim_set(st, "multicast_received", IFA_DATA(imcasts)); - rrddim_set(st, "multicast_sent", IFA_DATA(omcasts)); - rrdset_done(st); - - // -------------------------------------------------------------------- - - st = rrdset_find_bytype("net_errors", ifa->ifa_name); - if (unlikely(!st)) { - st = rrdset_create("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "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", IFA_DATA(ierrors)); - rrddim_set(st, "outbound", IFA_DATA(oerrors)); - rrdset_done(st); - - // -------------------------------------------------------------------- - - st = rrdset_find_bytype("net_drops", ifa->ifa_name); - if (unlikely(!st)) { - st = rrdset_create("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; - - rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); -#if __FreeBSD__ >= 11 - rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); -#endif - } - else rrdset_next(st); + return 0; +} - rrddim_set(st, "inbound", IFA_DATA(iqdrops)); -#if __FreeBSD__ >= 11 - rrddim_set(st, "outbound", IFA_DATA(oqdrops)); -#endif - rrdset_done(st); +// -------------------------------------------------------------------------------------------------------------------- +// net.inet.tcp.states - // -------------------------------------------------------------------- +int do_net_inet_tcp_states(int update_every, usec_t dt) { + (void)dt; + static int mib[4] = {0, 0, 0, 0}; + uint64_t tcps_states[TCP_NSTATES]; - st = rrdset_find_bytype("net_events", ifa->ifa_name); - if (unlikely(!st)) { - st = rrdset_create("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html + if (unlikely(GETSYSCTL_SIMPLE("net.inet.tcp.states", mib, tcps_states))) { + error("DISABLED: ipv4.tcpsock chart"); + error("DISABLED: net.inet.tcp.states module"); + return 1; + } else { - 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, "collisions", IFA_DATA(collisions)); - rrdset_done(st); - } + static RRDSET *st = NULL; + static RRDDIM *rd = NULL; - freeifaddrs(ifap); - } + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "tcpsock", + NULL, + "tcp", + NULL, + "IPv4 TCP Connections", + "active connections", + 2500, + update_every, + RRDSET_TYPE_LINE + ); + + rd = rrddim_add(st, "CurrEstab", "connections", 1, 1, RRD_ALGORITHM_ABSOLUTE); + } else + rrdset_next(st); + + rrddim_set_by_pointer(st, rd, tcps_states[TCPS_ESTABLISHED]); + rrdset_done(st); } - // -------------------------------------------------------------------- - - // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html - if (likely(do_tcp_sockets)) { - if (unlikely(GETSYSCTL("net.inet.tcp.states", tcps_states))) { - do_tcp_sockets = 0; - error("DISABLED: ipv4.tcpsock"); - } else { - if (likely(do_tcp_sockets)) { - st = rrdset_find("ipv4.tcpsock"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections", - "active connections", 2500, update_every, RRDSET_TYPE_LINE); + return 0; +} - rrddim_add(st, "CurrEstab", "connections", 1, 1, RRDDIM_ABSOLUTE); - } else - rrdset_next(st); +// -------------------------------------------------------------------------------------------------------------------- +// net.inet.tcp.stats - rrddim_set(st, "CurrEstab", tcps_states[TCPS_ESTABLISHED]); - rrdset_done(st); - } - } +int do_net_inet_tcp_stats(int update_every, usec_t dt) { + (void)dt; + static int do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1, do_tcpext_connaborts = -1, do_tcpext_ofo = -1, do_tcpext_syncookies = -1, do_ecn = -1; + + if (unlikely(do_tcp_packets == -1)) { + do_tcp_packets = config_get_boolean("plugin:freebsd:net.inet.tcp.stats", "ipv4 TCP packets", 1); + do_tcp_errors = config_get_boolean("plugin:freebsd:net.inet.tcp.stats", "ipv4 TCP errors", 1); + do_tcp_handshake = config_get_boolean("plugin:freebsd:net.inet.tcp.stats", "ipv4 TCP handshake issues", 1); + do_tcpext_connaborts = config_get_boolean_ondemand("plugin:freebsd:net.inet.tcp.stats", "TCP connection aborts", + CONFIG_BOOLEAN_AUTO); + do_tcpext_ofo = config_get_boolean_ondemand("plugin:freebsd:net.inet.tcp.stats", "TCP out-of-order queue", + CONFIG_BOOLEAN_AUTO); + do_tcpext_syncookies = config_get_boolean_ondemand("plugin:freebsd:net.inet.tcp.stats", "TCP SYN cookies", + CONFIG_BOOLEAN_AUTO); + do_ecn = config_get_boolean_ondemand("plugin:freebsd:net.inet.tcp.stats", "ECN packets", + CONFIG_BOOLEAN_AUTO); } - // -------------------------------------------------------------------- - // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html - if (likely(do_tcp_packets || do_tcp_errors || do_tcp_handshake || do_tcpext_connaborts || do_tcpext_ofo || do_tcpext_syscookies || do_ecn)) { - if (unlikely(GETSYSCTL("net.inet.tcp.stats", tcpstat))){ + if (likely(do_tcp_packets || do_tcp_errors || do_tcp_handshake || do_tcpext_connaborts || do_tcpext_ofo || do_tcpext_syncookies || do_ecn)) { + static int mib[4] = {0, 0, 0, 0}; + struct tcpstat tcpstat; + + if (unlikely(GETSYSCTL_SIMPLE("net.inet.tcp.stats", mib, tcpstat))) { do_tcp_packets = 0; - error("DISABLED: ipv4.tcppackets"); + error("DISABLED: ipv4.tcppackets chart"); do_tcp_errors = 0; - error("DISABLED: ipv4.tcperrors"); + error("DISABLED: ipv4.tcperrors chart"); do_tcp_handshake = 0; - error("DISABLED: ipv4.tcphandshake"); + error("DISABLED: ipv4.tcphandshake chart"); do_tcpext_connaborts = 0; - error("DISABLED: ipv4.tcpconnaborts"); + error("DISABLED: ipv4.tcpconnaborts chart"); do_tcpext_ofo = 0; - error("DISABLED: ipv4.tcpofo"); - do_tcpext_syscookies = 0; - error("DISABLED: ipv4.tcpsyncookies"); + error("DISABLED: ipv4.tcpofo chart"); + do_tcpext_syncookies = 0; + error("DISABLED: ipv4.tcpsyncookies chart"); do_ecn = 0; - error("DISABLED: ipv4.ecnpkts"); + error("DISABLED: ipv4.ecnpkts chart"); + error("DISABLED: net.inet.tcp.stats module"); + return 1; } else { + + // -------------------------------------------------------------------- + if (likely(do_tcp_packets)) { - st = rrdset_find("ipv4.tcppackets"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", - "packets/s", - 2600, update_every, RRDSET_TYPE_LINE); + static RRDSET *st = NULL; + static RRDDIM *rd_in_segs = NULL, *rd_out_segs = NULL; - rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "tcppackets", + NULL, + "tcp", + NULL, + "IPv4 TCP Packets", + "packets/s", + 2600, + update_every, + RRDSET_TYPE_LINE + ); + + rd_in_segs = rrddim_add(st, "InSegs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_segs = rrddim_add(st, "OutSegs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InSegs", tcpstat.tcps_rcvtotal); - rrddim_set(st, "OutSegs", tcpstat.tcps_sndtotal); + rrddim_set_by_pointer(st, rd_in_segs, tcpstat.tcps_rcvtotal); + rrddim_set_by_pointer(st, rd_out_segs, tcpstat.tcps_sndtotal); rrdset_done(st); } // -------------------------------------------------------------------- if (likely(do_tcp_errors)) { - st = rrdset_find("ipv4.tcperrors"); + static RRDSET *st = NULL; + static RRDDIM *rd_in_errs = NULL, *rd_in_csum_errs = NULL, *rd_retrans_segs = NULL; + if (unlikely(!st)) { - st = rrdset_create("ipv4", "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, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv4", + "tcperrors", + NULL, + "tcp", + NULL, + "IPv4 TCP Errors", + "packets/s", + 2700, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_in_errs = rrddim_add(st, "InErrs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_csum_errs = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_retrans_segs = rrddim_add(st, "RetransSegs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); #if __FreeBSD__ >= 11 - rrddim_set(st, "InErrs", tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvreassfull + tcpstat.tcps_rcvshort); + rrddim_set_by_pointer(st, rd_in_errs, tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvreassfull + + tcpstat.tcps_rcvshort); #else - rrddim_set(st, "InErrs", tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvshort); + rrddim_set_by_pointer(st, rd_in_errs, tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvshort); #endif - rrddim_set(st, "InCsumErrors", tcpstat.tcps_rcvbadsum); - rrddim_set(st, "RetransSegs", tcpstat.tcps_sndrexmitpack); + rrddim_set_by_pointer(st, rd_in_csum_errs, tcpstat.tcps_rcvbadsum); + rrddim_set_by_pointer(st, rd_retrans_segs, tcpstat.tcps_sndrexmitpack); rrdset_done(st); } // -------------------------------------------------------------------- if (likely(do_tcp_handshake)) { - st = rrdset_find("ipv4.tcphandshake"); + static RRDSET *st = NULL; + static RRDDIM *rd_estab_resets = NULL, *rd_active_opens = NULL, *rd_passive_opens = NULL, + *rd_attempt_fails = NULL; + if (unlikely(!st)) { - st = rrdset_create("ipv4", "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, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv4", + "tcphandshake", + NULL, + "tcp", + NULL, + "IPv4 TCP Handshake Issues", + "events/s", + 2900, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_estab_resets = rrddim_add(st, "EstabResets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_active_opens = rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_passive_opens = rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_attempt_fails = rrddim_add(st, "AttemptFails", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "EstabResets", tcpstat.tcps_drops); - rrddim_set(st, "ActiveOpens", tcpstat.tcps_connattempt); - rrddim_set(st, "PassiveOpens", tcpstat.tcps_accepts); - rrddim_set(st, "AttemptFails", tcpstat.tcps_conndrops); + rrddim_set_by_pointer(st, rd_estab_resets, tcpstat.tcps_drops); + rrddim_set_by_pointer(st, rd_active_opens, tcpstat.tcps_connattempt); + rrddim_set_by_pointer(st, rd_passive_opens, tcpstat.tcps_accepts); + rrddim_set_by_pointer(st, rd_attempt_fails, tcpstat.tcps_conndrops); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop || tcpstat.tcps_finwait2_drops))) { - do_tcpext_connaborts = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.tcpconnaborts"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE); + if (do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop || tcpstat.tcps_finwait2_drops))) { + do_tcpext_connaborts = CONFIG_BOOLEAN_YES; - rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnClose", "userclosed", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnLinger", "linger", 1, 1, RRDDIM_INCREMENTAL); + static RRDSET *st = NULL; + static RRDDIM *rd_on_data = NULL, *rd_on_close = NULL, *rd_on_memory = NULL, + *rd_on_timeout = NULL, *rd_on_linger = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "tcpconnaborts", + NULL, + "tcp", + NULL, + "TCP Connection Aborts", + "connections/s", + 3010, + update_every, + RRDSET_TYPE_LINE + ); + + rd_on_data = rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_on_close = rrddim_add(st, "TCPAbortOnClose", "userclosed", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_on_memory = rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_on_timeout = rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_on_linger = rrddim_add(st, "TCPAbortOnLinger", "linger", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "TCPAbortOnData", tcpstat.tcps_rcvpackafterwin); - rrddim_set(st, "TCPAbortOnClose", tcpstat.tcps_rcvafterclose); - rrddim_set(st, "TCPAbortOnMemory", tcpstat.tcps_rcvmemdrop); - rrddim_set(st, "TCPAbortOnTimeout", tcpstat.tcps_persistdrop); - rrddim_set(st, "TCPAbortOnLinger", tcpstat.tcps_finwait2_drops); + rrddim_set_by_pointer(st, rd_on_data, tcpstat.tcps_rcvpackafterwin); + rrddim_set_by_pointer(st, rd_on_close, tcpstat.tcps_rcvafterclose); + rrddim_set_by_pointer(st, rd_on_memory, tcpstat.tcps_rcvmemdrop); + rrddim_set_by_pointer(st, rd_on_timeout, tcpstat.tcps_persistdrop); + rrddim_set_by_pointer(st, rd_on_linger, tcpstat.tcps_finwait2_drops); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && tcpstat.tcps_rcvoopack)) { - do_tcpext_ofo = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.tcpofo"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE); + if (do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && tcpstat.tcps_rcvoopack)) { + do_tcpext_ofo = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_ofo_queue = NULL; - rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRDDIM_INCREMENTAL); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "tcpofo", + NULL, + "tcp", + NULL, + "TCP Out-Of-Order Queue", + "packets/s", + 3050, + update_every, + RRDSET_TYPE_LINE + ); + + rd_ofo_queue = rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "TCPOFOQueue", tcpstat.tcps_rcvoopack); + rrddim_set_by_pointer(st, rd_ofo_queue, tcpstat.tcps_rcvoopack); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) { - do_tcpext_syscookies = CONFIG_ONDEMAND_YES; + if (do_tcpext_syncookies == CONFIG_BOOLEAN_YES || (do_tcpext_syncookies == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) { + do_tcpext_syncookies = CONFIG_BOOLEAN_YES; - st = rrdset_find("ipv4.tcpsyncookies"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE); + static RRDSET *st = NULL; + static RRDDIM *rd_recv = NULL, *rd_send = NULL, *rd_failed = NULL; - rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRDDIM_INCREMENTAL); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "tcpsyncookies", + NULL, + "tcp", + NULL, + "TCP SYN Cookies", + "packets/s", + 3100, + update_every, + RRDSET_TYPE_LINE + ); + + rd_recv = rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_send = rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_failed = rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "SyncookiesRecv", tcpstat.tcps_sc_recvcookie); - rrddim_set(st, "SyncookiesSent", tcpstat.tcps_sc_sendcookie); - rrddim_set(st, "SyncookiesFailed", tcpstat.tcps_sc_zonefail); + rrddim_set_by_pointer(st, rd_recv, tcpstat.tcps_sc_recvcookie); + rrddim_set_by_pointer(st, rd_send, tcpstat.tcps_sc_sendcookie); + rrddim_set_by_pointer(st, rd_failed, tcpstat.tcps_sc_zonefail); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_ecn_ce || tcpstat.tcps_ecn_ect0 || tcpstat.tcps_ecn_ect1))) { - do_ecn = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.ecnpkts"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + if (do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_ecn_ce || tcpstat.tcps_ecn_ect0 || tcpstat.tcps_ecn_ect1))) { + do_ecn = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_ce = NULL, *rd_no_ect = NULL, *rd_ect0 = NULL, *rd_ect1 = NULL; - rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRDDIM_INCREMENTAL); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "ecnpkts", + NULL, + "ecn", + NULL, + "IPv4 ECN Statistics", + "packets/s", + 8700, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_ce = rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_no_ect = rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ect0 = rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ect1 = rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InCEPkts", tcpstat.tcps_ecn_ce); - rrddim_set(st, "InNoECTPkts", tcpstat.tcps_ecn_ce - (tcpstat.tcps_ecn_ect0 + tcpstat.tcps_ecn_ect1)); - rrddim_set(st, "InECT0Pkts", tcpstat.tcps_ecn_ect0); - rrddim_set(st, "InECT1Pkts", tcpstat.tcps_ecn_ect1); + rrddim_set_by_pointer(st, rd_ce, tcpstat.tcps_ecn_ce); + rrddim_set_by_pointer(st, rd_no_ect, tcpstat.tcps_ecn_ce - (tcpstat.tcps_ecn_ect0 + + tcpstat.tcps_ecn_ect1)); + rrddim_set_by_pointer(st, rd_ect0, tcpstat.tcps_ecn_ect0); + rrddim_set_by_pointer(st, rd_ect1, tcpstat.tcps_ecn_ect1); rrdset_done(st); } } + } else { + error("DISABLED: net.inet.tcp.stats module"); + return 1; } - // -------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// net.inet.udp.stats + +int do_net_inet_udp_stats(int update_every, usec_t dt) { + (void)dt; + static int do_udp_packets = -1, do_udp_errors = -1; + + if (unlikely(do_udp_packets == -1)) { + do_udp_packets = config_get_boolean("plugin:freebsd:net.inet.udp.stats", "ipv4 UDP packets", 1); + do_udp_errors = config_get_boolean("plugin:freebsd:net.inet.udp.stats", "ipv4 UDP errors", 1); + } // see http://net-snmp.sourceforge.net/docs/mibs/udp.html if (likely(do_udp_packets || do_udp_errors)) { - if (unlikely(GETSYSCTL("net.inet.udp.stats", udpstat))) { + static int mib[4] = {0, 0, 0, 0}; + struct udpstat udpstat; + + if (unlikely(GETSYSCTL_SIMPLE("net.inet.udp.stats", mib, udpstat))) { do_udp_packets = 0; - error("DISABLED: ipv4.udppackets"); + error("DISABLED: ipv4.udppackets chart"); do_udp_errors = 0; - error("DISABLED: ipv4.udperrors"); + error("DISABLED: ipv4.udperrors chart"); + error("DISABLED: net.inet.udp.stats module"); + return 1; } else { + + // -------------------------------------------------------------------- + if (likely(do_udp_packets)) { - st = rrdset_find("ipv4.udppackets"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", - "packets/s", 2601, update_every, RRDSET_TYPE_LINE); + static RRDSET *st = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; - rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "udppackets", + NULL, + "udp", + NULL, + "IPv4 UDP Packets", + "packets/s", + 2601, + update_every, + RRDSET_TYPE_LINE + ); + + rd_in = rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InDatagrams", udpstat.udps_ipackets); - rrddim_set(st, "OutDatagrams", udpstat.udps_opackets); + rrddim_set_by_pointer(st, rd_in, udpstat.udps_ipackets); + rrddim_set_by_pointer(st, rd_out, udpstat.udps_opackets); rrdset_done(st); } // -------------------------------------------------------------------- if (likely(do_udp_errors)) { - st = rrdset_find("ipv4.udperrors"); + static RRDSET *st = NULL; + static RRDDIM *rd_in_errors = NULL, *rd_no_ports = NULL, *rd_recv_buf_errors = NULL, + *rd_in_csum_errors = NULL, *rd_ignored_multi = NULL; + if (unlikely(!st)) { - st = rrdset_create("ipv4", "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, "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); + st = rrdset_create_localhost("ipv4", + "udperrors", + NULL, + "udp", + NULL, + "IPv4 UDP Errors", + "events/s", + 2701, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_in_errors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_no_ports = rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_recv_buf_errors = rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_csum_errors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ignored_multi = rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InErrors", udpstat.udps_hdrops + udpstat.udps_badlen); - rrddim_set(st, "NoPorts", udpstat.udps_noport); - rrddim_set(st, "RcvbufErrors", udpstat.udps_fullsock); - rrddim_set(st, "InCsumErrors", udpstat.udps_badsum + udpstat.udps_nosum); - rrddim_set(st, "IgnoredMulti", udpstat.udps_filtermcast); + rrddim_set_by_pointer(st, rd_in_errors, udpstat.udps_hdrops + udpstat.udps_badlen); + rrddim_set_by_pointer(st, rd_no_ports, udpstat.udps_noport); + rrddim_set_by_pointer(st, rd_recv_buf_errors, udpstat.udps_fullsock); + rrddim_set_by_pointer(st, rd_in_csum_errors, udpstat.udps_badsum + udpstat.udps_nosum); + rrddim_set_by_pointer(st, rd_ignored_multi, udpstat.udps_filtermcast); rrdset_done(st); } } + } else { + error("DISABLED: net.inet.udp.stats module"); + return 1; } - // -------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// net.inet.icmp.stats + +int do_net_inet_icmp_stats(int update_every, usec_t dt) { + (void)dt; + static int do_icmp_packets = -1, do_icmp_errors = -1, do_icmpmsg = -1; + + if (unlikely(do_icmp_packets == -1)) { + do_icmp_packets = config_get_boolean("plugin:freebsd:net.inet.icmp.stats", "ipv4 ICMP packets", 1); + do_icmp_errors = config_get_boolean("plugin:freebsd:net.inet.icmp.stats", "ipv4 ICMP errors", 1); + do_icmpmsg = config_get_boolean("plugin:freebsd:net.inet.icmp.stats", "ipv4 ICMP messages", 1); + } - if (likely(do_icmp_packets || do_icmpmsg)) { - if (unlikely(GETSYSCTL("net.inet.icmp.stats", icmpstat))) { + if (likely(do_icmp_packets || do_icmp_errors || do_icmpmsg)) { + static int mib[4] = {0, 0, 0, 0}; + struct icmpstat icmpstat; + int i; + struct icmp_total { + u_long msgs_in; + u_long msgs_out; + } icmp_total = {0, 0}; + + if (unlikely(GETSYSCTL_SIMPLE("net.inet.icmp.stats", mib, icmpstat))) { do_icmp_packets = 0; - error("DISABLED: ipv4.icmp"); - error("DISABLED: ipv4.icmp_errors"); + error("DISABLED: ipv4.icmp chart"); + do_icmp_errors = 0; + error("DISABLED: ipv4.icmp_errors chart"); do_icmpmsg = 0; - error("DISABLED: ipv4.icmpmsg"); + error("DISABLED: ipv4.icmpmsg chart"); + error("DISABLED: net.inet.icmp.stats module"); + return 1; } else { for (i = 0; i <= ICMP_MAXTYPE; i++) { icmp_total.msgs_in += icmpstat.icps_inhist[i]; @@ -1592,39 +1902,54 @@ int do_freebsd_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_icmp_packets)) { - st = rrdset_find("ipv4.icmp"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s", - 2602, - update_every, RRDSET_TYPE_LINE); + static RRDSET *st = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; - rrddim_add(st, "InMsgs", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutMsgs", "sent", -1, 1, RRDDIM_INCREMENTAL); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "icmp", + NULL, + "icmp", + NULL, + "IPv4 ICMP Packets", + "packets/s", + 2602, + update_every, + RRDSET_TYPE_LINE + ); + + rd_in = rrddim_add(st, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InMsgs", icmp_total.msgs_in); - rrddim_set(st, "OutMsgs", icmp_total.msgs_out); + rrddim_set_by_pointer(st, rd_in, icmp_total.msgs_in); + rrddim_set_by_pointer(st, rd_out, icmp_total.msgs_out); rrdset_done(st); + } - // -------------------------------------------------------------------- + // -------------------------------------------------------------------- + + if (likely(do_icmp_errors)) { + static RRDSET *st = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL, *rd_in_csum = NULL; - st = rrdset_find("ipv4.icmp_errors"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors", - "packets/s", - 2603, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors", + "packets/s", + 2603, 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); + rd_in = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_csum = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InErrors", icmpstat.icps_badcode + icmpstat.icps_badlen + icmpstat.icps_checksum + icmpstat.icps_tooshort); - rrddim_set(st, "OutErrors", icmpstat.icps_error); - rrddim_set(st, "InCsumErrors", icmpstat.icps_checksum); + rrddim_set_by_pointer(st, rd_in, icmpstat.icps_badcode + icmpstat.icps_badlen + + icmpstat.icps_checksum + icmpstat.icps_tooshort); + rrddim_set_by_pointer(st, rd_out, icmpstat.icps_error); + rrddim_set_by_pointer(st, rd_in_csum, icmpstat.icps_checksum); rrdset_done(st); } @@ -1632,226 +1957,370 @@ int do_freebsd_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_icmpmsg)) { - st = rrdset_find("ipv4.icmpmsg"); + static RRDSET *st = NULL; + static RRDDIM *rd_in_reps = NULL, *rd_out_reps = NULL, *rd_in = NULL, *rd_out = NULL; + if (unlikely(!st)) { - st = rrdset_create("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages", - "packets/s", 2604, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages", + "packets/s", 2604, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InEchoReps", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL); + rd_in_reps = rrddim_add(st, "InEchoReps", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_reps = rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in = rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InEchoReps", icmpstat.icps_inhist[ICMP_ECHOREPLY]); - rrddim_set(st, "OutEchoReps", icmpstat.icps_outhist[ICMP_ECHOREPLY]); - rrddim_set(st, "InEchos", icmpstat.icps_inhist[ICMP_ECHO]); - rrddim_set(st, "OutEchos", icmpstat.icps_outhist[ICMP_ECHO]); + rrddim_set_by_pointer(st, rd_in_reps, icmpstat.icps_inhist[ICMP_ECHOREPLY]); + rrddim_set_by_pointer(st, rd_out_reps, icmpstat.icps_outhist[ICMP_ECHOREPLY]); + rrddim_set_by_pointer(st, rd_in, icmpstat.icps_inhist[ICMP_ECHO]); + rrddim_set_by_pointer(st, rd_out, icmpstat.icps_outhist[ICMP_ECHO]); rrdset_done(st); } } + } else { + error("DISABLED: net.inet.icmp.stats module"); + return 1; } - // -------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// net.inet.ip.stats + +int do_net_inet_ip_stats(int update_every, usec_t dt) { + (void)dt; + static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1; + + if (unlikely(do_ip_packets == -1)) { + do_ip_packets = config_get_boolean("plugin:freebsd:net.inet.ip.stats", "ipv4 packets", 1); + do_ip_fragsout = config_get_boolean("plugin:freebsd:net.inet.ip.stats", "ipv4 fragments sent", 1); + do_ip_fragsin = config_get_boolean("plugin:freebsd:net.inet.ip.stats", "ipv4 fragments assembly", 1); + do_ip_errors = config_get_boolean("plugin:freebsd:net.inet.ip.stats", "ipv4 errors", 1); + } // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html if (likely(do_ip_packets || do_ip_fragsout || do_ip_fragsin || do_ip_errors)) { - if (unlikely(GETSYSCTL("net.inet.ip.stats", ipstat))) { + static int mib[4] = {0, 0, 0, 0}; + struct ipstat ipstat; + + if (unlikely(GETSYSCTL_SIMPLE("net.inet.ip.stats", mib, ipstat))) { do_ip_packets = 0; - error("DISABLED: ipv4.packets"); + error("DISABLED: ipv4.packets chart"); do_ip_fragsout = 0; - error("DISABLED: ipv4.fragsout"); + error("DISABLED: ipv4.fragsout chart"); do_ip_fragsin = 0; - error("DISABLED: ipv4.fragsin"); + error("DISABLED: ipv4.fragsin chart"); do_ip_errors = 0; - error("DISABLED: ipv4.errors"); + error("DISABLED: ipv4.errors chart"); + error("DISABLED: net.inet.ip.stats module"); + return 1; } else { + + // -------------------------------------------------------------------- + if (likely(do_ip_packets)) { - st = rrdset_find("ipv4.packets"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", - 3000, update_every, RRDSET_TYPE_LINE); + static RRDSET *st = NULL; + static RRDDIM *rd_in_receives = NULL, *rd_out_requests = NULL, *rd_forward_datagrams = NULL, + *rd_in_delivers = NULL; - rrddim_add(st, "InReceives", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutRequests", "sent", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InDelivers", "delivered", 1, 1, RRDDIM_INCREMENTAL); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "packets", + NULL, + "packets", + NULL, + "IPv4 Packets", + "packets/s", + 3000, + update_every, + RRDSET_TYPE_LINE + ); + + rd_in_receives = rrddim_add(st, "InReceives", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_requests = rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_forward_datagrams = rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_delivers = rrddim_add(st, "InDelivers", "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "OutRequests", ipstat.ips_localout); - rrddim_set(st, "InReceives", ipstat.ips_total); - rrddim_set(st, "ForwDatagrams", ipstat.ips_forward); - rrddim_set(st, "InDelivers", ipstat.ips_delivered); + rrddim_set_by_pointer(st, rd_in_receives, ipstat.ips_total); + rrddim_set_by_pointer(st, rd_out_requests, ipstat.ips_localout); + rrddim_set_by_pointer(st, rd_forward_datagrams, ipstat.ips_forward); + rrddim_set_by_pointer(st, rd_in_delivers, ipstat.ips_delivered); rrdset_done(st); } // -------------------------------------------------------------------- if (likely(do_ip_fragsout)) { - st = rrdset_find("ipv4.fragsout"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", - "packets/s", 3010, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + static RRDSET *st = NULL; + static RRDDIM *rd_ok = NULL, *rd_fails = NULL, *rd_created = NULL; - rrddim_add(st, "FragOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "FragFails", "failed", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "FragCreates", "created", 1, 1, RRDDIM_INCREMENTAL); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "fragsout", + NULL, + "fragments", + NULL, + "IPv4 Fragments Sent", + "packets/s", + 3010, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_ok = rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_fails = rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_created = rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "FragOKs", ipstat.ips_fragmented); - rrddim_set(st, "FragFails", ipstat.ips_cantfrag); - rrddim_set(st, "FragCreates", ipstat.ips_ofragments); + rrddim_set_by_pointer(st, rd_ok, ipstat.ips_fragmented); + rrddim_set_by_pointer(st, rd_fails, ipstat.ips_cantfrag); + rrddim_set_by_pointer(st, rd_created, ipstat.ips_ofragments); rrdset_done(st); } // -------------------------------------------------------------------- if (likely(do_ip_fragsin)) { - st = rrdset_find("ipv4.fragsin"); + static RRDSET *st = NULL; + static RRDDIM *rd_ok = NULL, *rd_failed = NULL, *rd_all = NULL; + if (unlikely(!st)) { - st = rrdset_create("ipv4", "fragsin", NULL, "fragments", NULL, - "IPv4 Fragments Reassembly", - "packets/s", 3011, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; - - rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "ReasmFails", "failed", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "ReasmReqds", "all", 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv4", + "fragsin", + NULL, + "fragments", + NULL, + "IPv4 Fragments Reassembly", + "packets/s", + 3011, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_ok = rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_failed = rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_all = rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "ReasmOKs", ipstat.ips_fragments); - rrddim_set(st, "ReasmFails", ipstat.ips_fragdropped); - rrddim_set(st, "ReasmReqds", ipstat.ips_reassembled); + rrddim_set_by_pointer(st, rd_ok, ipstat.ips_fragments); + rrddim_set_by_pointer(st, rd_failed, ipstat.ips_fragdropped); + rrddim_set_by_pointer(st, rd_all, ipstat.ips_reassembled); rrdset_done(st); } // -------------------------------------------------------------------- if (likely(do_ip_errors)) { - st = rrdset_find("ipv4.errors"); - if (unlikely(!st)) { - st = rrdset_create("ipv4", "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, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL); + static RRDSET *st = NULL; + static RRDDIM *rd_in_discards = NULL, *rd_out_discards = NULL, + *rd_in_hdr_errors = NULL, *rd_out_no_routes = NULL, + *rd_in_addr_errors = NULL, *rd_in_unknown_protos = NULL; - rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv4", + "errors", + NULL, + "errors", + NULL, + "IPv4 Errors", + "packets/s", + 3002, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_in_discards = rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_discards = rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_hdr_errors = rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_no_routes = rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_addr_errors = rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_unknown_protos = rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InDiscards", ipstat.ips_badsum + ipstat.ips_tooshort + ipstat.ips_toosmall + ipstat.ips_toolong); - rrddim_set(st, "OutDiscards", ipstat.ips_odropped); - rrddim_set(st, "InHdrErrors", ipstat.ips_badhlen + ipstat.ips_badlen + ipstat.ips_badoptions + ipstat.ips_badvers); - rrddim_set(st, "InAddrErrors", ipstat.ips_badaddr); - rrddim_set(st, "InUnknownProtos", ipstat.ips_noproto); - rrddim_set(st, "OutNoRoutes", ipstat.ips_noroute); + rrddim_set_by_pointer(st, rd_in_discards, ipstat.ips_badsum + ipstat.ips_tooshort + + ipstat.ips_toosmall + ipstat.ips_toolong); + rrddim_set_by_pointer(st, rd_out_discards, ipstat.ips_odropped); + rrddim_set_by_pointer(st, rd_in_hdr_errors, ipstat.ips_badhlen + ipstat.ips_badlen + + ipstat.ips_badoptions + ipstat.ips_badvers); + rrddim_set_by_pointer(st, rd_out_no_routes, ipstat.ips_noroute); + rrddim_set_by_pointer(st, rd_in_addr_errors, ipstat.ips_badaddr); + rrddim_set_by_pointer(st, rd_in_unknown_protos, ipstat.ips_noproto); rrdset_done(st); } } + } else { + error("DISABLED: net.inet.ip.stats module"); + return 1; } - // -------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// net.inet6.ip6.stats + +int do_net_inet6_ip6_stats(int update_every, usec_t dt) { + (void)dt; + static int do_ip6_packets = -1, do_ip6_fragsout = -1, do_ip6_fragsin = -1, do_ip6_errors = -1; + + if (unlikely(do_ip6_packets == -1)) { + do_ip6_packets = config_get_boolean_ondemand("plugin:freebsd:net.inet6.ip6.stats", "ipv6 packets", + CONFIG_BOOLEAN_AUTO); + do_ip6_fragsout = config_get_boolean_ondemand("plugin:freebsd:net.inet6.ip6.stats", "ipv6 fragments sent", + CONFIG_BOOLEAN_AUTO); + do_ip6_fragsin = config_get_boolean_ondemand("plugin:freebsd:net.inet6.ip6.stats", "ipv6 fragments assembly", + CONFIG_BOOLEAN_AUTO); + do_ip6_errors = config_get_boolean_ondemand("plugin:freebsd:net.inet6.ip6.stats", "ipv6 errors", + CONFIG_BOOLEAN_AUTO); + } if (likely(do_ip6_packets || do_ip6_fragsout || do_ip6_fragsin || do_ip6_errors)) { - if (unlikely(GETSYSCTL("net.inet6.ip6.stats", ip6stat))) { + static int mib[4] = {0, 0, 0, 0}; + struct ip6stat ip6stat; + + if (unlikely(GETSYSCTL_SIMPLE("net.inet6.ip6.stats", mib, ip6stat))) { do_ip6_packets = 0; - error("DISABLED: ipv6.packets"); + error("DISABLED: ipv6.packets chart"); do_ip6_fragsout = 0; - error("DISABLED: ipv6.fragsout"); + error("DISABLED: ipv6.fragsout chart"); do_ip6_fragsin = 0; - error("DISABLED: ipv6.fragsin"); + error("DISABLED: ipv6.fragsin chart"); do_ip6_errors = 0; - error("DISABLED: ipv6.errors"); + error("DISABLED: ipv6.errors chart"); + error("DISABLED: net.inet6.ip6.stats module"); + return 1; } else { - if (do_ip6_packets == CONFIG_ONDEMAND_YES || (do_ip6_packets == CONFIG_ONDEMAND_ONDEMAND && - (ip6stat.ip6s_localout || ip6stat.ip6s_total || - ip6stat.ip6s_forward || ip6stat.ip6s_delivered))) { - do_ip6_packets = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.packets"); - if (unlikely(!st)) { - st = rrdset_create("ipv6", "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); + // -------------------------------------------------------------------- + + if (do_ip6_packets == CONFIG_BOOLEAN_YES || (do_ip6_packets == CONFIG_BOOLEAN_AUTO && + (ip6stat.ip6s_localout || ip6stat.ip6s_total || + ip6stat.ip6s_forward || ip6stat.ip6s_delivered))) { + do_ip6_packets = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_received = NULL, *rd_sent = NULL, *rd_forwarded = NULL, *rd_delivers = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv6", + "packets", + NULL, + "packets", + NULL, + "IPv6 Packets", + "packets/s", + 3000, + update_every, + RRDSET_TYPE_LINE + ); + + rd_received = rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_sent = rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_forwarded = rrddim_add(st, "forwarded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_delivers = rrddim_add(st, "delivers", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "sent", ip6stat.ip6s_localout); - rrddim_set(st, "received", ip6stat.ip6s_total); - rrddim_set(st, "forwarded", ip6stat.ip6s_forward); - rrddim_set(st, "delivers", ip6stat.ip6s_delivered); + rrddim_set_by_pointer(st, rd_sent, ip6stat.ip6s_localout); + rrddim_set_by_pointer(st, rd_received, ip6stat.ip6s_total); + rrddim_set_by_pointer(st, rd_forwarded, ip6stat.ip6s_forward); + rrddim_set_by_pointer(st, rd_delivers, ip6stat.ip6s_delivered); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_ip6_fragsout == CONFIG_ONDEMAND_YES || (do_ip6_fragsout == CONFIG_ONDEMAND_ONDEMAND && - (ip6stat.ip6s_fragmented || ip6stat.ip6s_cantfrag || - ip6stat.ip6s_ofragments))) { - do_ip6_fragsout = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.fragsout"); - if (unlikely(!st)) { - st = rrdset_create("ipv6", "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", - "packets/s", 3010, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + if (do_ip6_fragsout == CONFIG_BOOLEAN_YES || (do_ip6_fragsout == CONFIG_BOOLEAN_AUTO && + (ip6stat.ip6s_fragmented || ip6stat.ip6s_cantfrag || + ip6stat.ip6s_ofragments))) { + do_ip6_fragsout = CONFIG_BOOLEAN_YES; - 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); + static RRDSET *st = NULL; + static RRDDIM *rd_ok = NULL, *rd_failed = NULL, *rd_all = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv6", + "fragsout", + NULL, + "fragments", + NULL, + "IPv6 Fragments Sent", + "packets/s", + 3010, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_ok = rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_failed = rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_all = rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "ok", ip6stat.ip6s_fragmented); - rrddim_set(st, "failed", ip6stat.ip6s_cantfrag); - rrddim_set(st, "all", ip6stat.ip6s_ofragments); + rrddim_set_by_pointer(st, rd_ok, ip6stat.ip6s_fragmented); + rrddim_set_by_pointer(st, rd_failed, ip6stat.ip6s_cantfrag); + rrddim_set_by_pointer(st, rd_all, ip6stat.ip6s_ofragments); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_ip6_fragsin == CONFIG_ONDEMAND_YES || (do_ip6_fragsin == CONFIG_ONDEMAND_ONDEMAND && - (ip6stat.ip6s_reassembled || ip6stat.ip6s_fragdropped || - ip6stat.ip6s_fragtimeout || ip6stat.ip6s_fragments))) { - do_ip6_fragsin = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.fragsin"); + if (do_ip6_fragsin == CONFIG_BOOLEAN_YES || (do_ip6_fragsin == CONFIG_BOOLEAN_AUTO && + (ip6stat.ip6s_reassembled || ip6stat.ip6s_fragdropped || + ip6stat.ip6s_fragtimeout || ip6stat.ip6s_fragments))) { + do_ip6_fragsin = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_ok = NULL, *rd_failed = NULL, *rd_timeout = NULL, *rd_all = NULL; + if (unlikely(!st)) { - st = rrdset_create("ipv6", "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); + st = rrdset_create_localhost("ipv6", + "fragsin", + NULL, + "fragments", + NULL, + "IPv6 Fragments Reassembly", + "packets/s", + 3011, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_ok = rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_failed = rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_timeout = rrddim_add(st, "timeout", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_all = rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "ok", ip6stat.ip6s_reassembled); - rrddim_set(st, "failed", ip6stat.ip6s_fragdropped); - rrddim_set(st, "timeout", ip6stat.ip6s_fragtimeout); - rrddim_set(st, "all", ip6stat.ip6s_fragments); + rrddim_set_by_pointer(st, rd_ok, ip6stat.ip6s_reassembled); + rrddim_set_by_pointer(st, rd_failed, ip6stat.ip6s_fragdropped); + rrddim_set_by_pointer(st, rd_timeout, ip6stat.ip6s_fragtimeout); + rrddim_set_by_pointer(st, rd_all, ip6stat.ip6s_fragments); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_ip6_errors == CONFIG_ONDEMAND_YES || (do_ip6_errors == CONFIG_ONDEMAND_ONDEMAND && ( + if (do_ip6_errors == CONFIG_BOOLEAN_YES || (do_ip6_errors == CONFIG_BOOLEAN_AUTO && ( ip6stat.ip6s_toosmall || ip6stat.ip6s_odropped || ip6stat.ip6s_badoptions || @@ -1861,346 +2330,1634 @@ int do_freebsd_sysctl(int update_every, usec_t dt) { ip6stat.ip6s_tooshort || ip6stat.ip6s_cantforward || ip6stat.ip6s_noroute))) { - do_ip6_errors = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.errors"); - if (unlikely(!st)) { - st = rrdset_create("ipv6", "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, - update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + do_ip6_errors = CONFIG_BOOLEAN_YES; - rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + static RRDSET *st = NULL; + static RRDDIM *rd_in_discards = NULL, *rd_out_discards = NULL, + *rd_in_hdr_errors = NULL, *rd_in_addr_errors = NULL, *rd_in_truncated_pkts = NULL, + *rd_in_no_routes = NULL, *rd_out_no_routes = NULL; - rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InAddrErrors", 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); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv6", + "errors", + NULL, + "errors", + NULL, + "IPv6 Errors", + "packets/s", + 3002, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rd_in_discards = rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_discards = rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_hdr_errors = rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_addr_errors = rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_truncated_pkts = rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_no_routes = rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_no_routes = rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InDiscards", ip6stat.ip6s_toosmall); - rrddim_set(st, "OutDiscards", ip6stat.ip6s_odropped); - - rrddim_set(st, "InHdrErrors", - ip6stat.ip6s_badoptions + ip6stat.ip6s_badvers + ip6stat.ip6s_exthdrtoolong); - rrddim_set(st, "InAddrErrors", ip6stat.ip6s_sources_none); - rrddim_set(st, "InTruncatedPkts", ip6stat.ip6s_tooshort); - rrddim_set(st, "InNoRoutes", ip6stat.ip6s_cantforward); - - rrddim_set(st, "OutNoRoutes", ip6stat.ip6s_noroute); + rrddim_set_by_pointer(st, rd_in_discards, ip6stat.ip6s_toosmall); + rrddim_set_by_pointer(st, rd_out_discards, ip6stat.ip6s_odropped); + rrddim_set_by_pointer(st, rd_in_hdr_errors, ip6stat.ip6s_badoptions + ip6stat.ip6s_badvers + + ip6stat.ip6s_exthdrtoolong); + rrddim_set_by_pointer(st, rd_in_addr_errors, ip6stat.ip6s_sources_none); + rrddim_set_by_pointer(st, rd_in_truncated_pkts, ip6stat.ip6s_tooshort); + rrddim_set_by_pointer(st, rd_in_no_routes, ip6stat.ip6s_cantforward); + rrddim_set_by_pointer(st, rd_out_no_routes, ip6stat.ip6s_noroute); rrdset_done(st); } } + } else { + error("DISABLED: net.inet6.ip6.stats module"); + return 1; } - // -------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// net.inet6.icmp6.stats + +int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { + (void)dt; + static int do_icmp6 = -1, do_icmp6_redir = -1, do_icmp6_errors = -1, do_icmp6_echos = -1, do_icmp6_router = -1, + do_icmp6_neighbor = -1, do_icmp6_types = -1; + + if (unlikely(do_icmp6 == -1)) { + do_icmp6 = config_get_boolean_ondemand("plugin:freebsd:net.inet6.icmp6.stats", "icmp", + CONFIG_BOOLEAN_AUTO); + do_icmp6_redir = config_get_boolean_ondemand("plugin:freebsd:net.inet6.icmp6.stats", "icmp redirects", + CONFIG_BOOLEAN_AUTO); + do_icmp6_errors = config_get_boolean_ondemand("plugin:freebsd:net.inet6.icmp6.stats", "icmp errors", + CONFIG_BOOLEAN_AUTO); + do_icmp6_echos = config_get_boolean_ondemand("plugin:freebsd:net.inet6.icmp6.stats", "icmp echos", + CONFIG_BOOLEAN_AUTO); + do_icmp6_router = config_get_boolean_ondemand("plugin:freebsd:net.inet6.icmp6.stats", "icmp router", + CONFIG_BOOLEAN_AUTO); + do_icmp6_neighbor = config_get_boolean_ondemand("plugin:freebsd:net.inet6.icmp6.stats", "icmp neighbor", + CONFIG_BOOLEAN_AUTO); + do_icmp6_types = config_get_boolean_ondemand("plugin:freebsd:net.inet6.icmp6.stats", "icmp types", + CONFIG_BOOLEAN_AUTO); + } if (likely(do_icmp6 || do_icmp6_redir || do_icmp6_errors || do_icmp6_echos || do_icmp6_router || do_icmp6_neighbor || do_icmp6_types)) { - if (unlikely(GETSYSCTL("net.inet6.icmp6.stats", icmp6stat))) { + static int mib[4] = {0, 0, 0, 0}; + struct icmp6stat icmp6stat; + + if (unlikely(GETSYSCTL_SIMPLE("net.inet6.icmp6.stats", mib, icmp6stat))) { do_icmp6 = 0; - error("DISABLED: ipv6.icmp"); + error("DISABLED: ipv6.icmp chart"); + do_icmp6_redir = 0; + error("DISABLED: ipv6.icmpredir chart"); + do_icmp6_errors = 0; + error("DISABLED: ipv6.icmperrors chart"); + do_icmp6_echos = 0; + error("DISABLED: ipv6.icmpechos chart"); + do_icmp6_router = 0; + error("DISABLED: ipv6.icmprouter chart"); + do_icmp6_neighbor = 0; + error("DISABLED: ipv6.icmpneighbor chart"); + do_icmp6_types = 0; + error("DISABLED: ipv6.icmptypes chart"); + error("DISABLED: net.inet6.icmp6.stats module"); + return 1; } else { + int i; + struct icmp6_total { + u_long msgs_in; + u_long msgs_out; + } icmp6_total = {0, 0}; + for (i = 0; i <= ICMP6_MAXTYPE; i++) { icmp6_total.msgs_in += icmp6stat.icp6s_inhist[i]; icmp6_total.msgs_out += icmp6stat.icp6s_outhist[i]; } icmp6_total.msgs_in += icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort; - if (do_icmp6 == CONFIG_ONDEMAND_YES || (do_icmp6 == CONFIG_ONDEMAND_ONDEMAND && (icmp6_total.msgs_in || icmp6_total.msgs_out))) { - do_icmp6 = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmp"); - if (unlikely(!st)) { - st = rrdset_create("ipv6", "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); + // -------------------------------------------------------------------- + + if (do_icmp6 == CONFIG_BOOLEAN_YES || (do_icmp6 == CONFIG_BOOLEAN_AUTO && (icmp6_total.msgs_in || icmp6_total.msgs_out))) { + do_icmp6 = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_received = NULL, *rd_sent = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv6", + "icmp", + NULL, + "icmp", + NULL, + "IPv6 ICMP Messages", + "messages/s", + 10000, + update_every, + RRDSET_TYPE_LINE + ); + + rd_received = rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_sent = rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "sent", icmp6_total.msgs_in); - rrddim_set(st, "received", icmp6_total.msgs_out); + rrddim_set_by_pointer(st, rd_received, icmp6_total.msgs_out); + rrddim_set_by_pointer(st, rd_sent, icmp6_total.msgs_in); + rrdset_done(st); } // -------------------------------------------------------------------- - if (do_icmp6_redir == CONFIG_ONDEMAND_YES || (do_icmp6_redir == CONFIG_ONDEMAND_ONDEMAND && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) { - do_icmp6_redir = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmpredir"); - if (unlikely(!st)) { - st = rrdset_create("ipv6", "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", - "redirects/s", 10050, update_every, RRDSET_TYPE_LINE); + if (do_icmp6_redir == CONFIG_BOOLEAN_YES || (do_icmp6_redir == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) { + do_icmp6_redir = CONFIG_BOOLEAN_YES; - rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + static RRDSET *st = NULL; + static RRDDIM *rd_received = NULL, *rd_sent = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv6", + "icmpredir", + NULL, + "icmp", + NULL, + "IPv6 ICMP Redirects", + "redirects/s", + 10050, + update_every, + RRDSET_TYPE_LINE + ); + + rd_received = rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_sent = rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "sent", icmp6stat.icp6s_inhist[ND_REDIRECT]); - rrddim_set(st, "received", icmp6stat.icp6s_outhist[ND_REDIRECT]); + rrddim_set_by_pointer(st, rd_received, icmp6stat.icp6s_outhist[ND_REDIRECT]); + rrddim_set_by_pointer(st, rd_sent, icmp6stat.icp6s_inhist[ND_REDIRECT]); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_icmp6_errors == CONFIG_ONDEMAND_YES || (do_icmp6_errors == CONFIG_ONDEMAND_ONDEMAND && ( - icmp6stat.icp6s_badcode || - icmp6stat.icp6s_badlen || - icmp6stat.icp6s_checksum || - icmp6stat.icp6s_tooshort || - icmp6stat.icp6s_error || - icmp6stat.icp6s_inhist[ICMP6_DST_UNREACH] || - icmp6stat.icp6s_inhist[ICMP6_TIME_EXCEEDED] || - icmp6stat.icp6s_inhist[ICMP6_PARAM_PROB] || - icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH] || - icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED] || - icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]))) { - do_icmp6_errors = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmperrors"); + if (do_icmp6_errors == CONFIG_BOOLEAN_YES || (do_icmp6_errors == CONFIG_BOOLEAN_AUTO && ( + icmp6stat.icp6s_badcode || + icmp6stat.icp6s_badlen || + icmp6stat.icp6s_checksum || + icmp6stat.icp6s_tooshort || + icmp6stat.icp6s_error || + icmp6stat.icp6s_inhist[ICMP6_DST_UNREACH] || + icmp6stat.icp6s_inhist[ICMP6_TIME_EXCEEDED] || + icmp6stat.icp6s_inhist[ICMP6_PARAM_PROB] || + icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH] || + icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED] || + icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]))) { + do_icmp6_errors = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_in_errors = NULL, *rd_out_errors = NULL, *rd_in_csum_errors = NULL, + *rd_in_dest_unreachs = NULL, *rd_in_pkt_too_bigs = NULL, *rd_in_time_excds = NULL, + *rd_in_parm_problems = NULL, *rd_out_dest_unreachs = NULL, *rd_out_time_excds = NULL, + *rd_out_parm_problems = NULL; + if (unlikely(!st)) { - st = rrdset_create("ipv6", "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, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv6", + "icmperrors", + NULL, "icmp", + NULL, + "IPv6 ICMP Errors", + "errors/s", + 10100, + update_every, + RRDSET_TYPE_LINE + ); + + rd_in_errors = rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_errors = rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_csum_errors = rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_dest_unreachs = rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_pkt_too_bigs = rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_time_excds = rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_parm_problems = rrddim_add(st, "InParmProblems", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_dest_unreachs = rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_time_excds = rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_parm_problems = rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InErrors", icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort); - rrddim_set(st, "OutErrors", icmp6stat.icp6s_error); - rrddim_set(st, "InCsumErrors", icmp6stat.icp6s_checksum); - rrddim_set(st, "InDestUnreachs", icmp6stat.icp6s_inhist[ICMP6_DST_UNREACH]); - rrddim_set(st, "InPktTooBigs", icmp6stat.icp6s_badlen); - rrddim_set(st, "InTimeExcds", icmp6stat.icp6s_inhist[ICMP6_TIME_EXCEEDED]); - rrddim_set(st, "InParmProblems", icmp6stat.icp6s_inhist[ICMP6_PARAM_PROB]); - rrddim_set(st, "OutDestUnreachs", icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH]); - rrddim_set(st, "OutTimeExcds", icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED]); - rrddim_set(st, "OutParmProblems", icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]); + rrddim_set_by_pointer(st, rd_in_errors, icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort); + rrddim_set_by_pointer(st, rd_out_errors, icmp6stat.icp6s_error); + rrddim_set_by_pointer(st, rd_in_csum_errors, icmp6stat.icp6s_checksum); + rrddim_set_by_pointer(st, rd_in_dest_unreachs, icmp6stat.icp6s_inhist[ICMP6_DST_UNREACH]); + rrddim_set_by_pointer(st, rd_in_pkt_too_bigs, icmp6stat.icp6s_badlen); + rrddim_set_by_pointer(st, rd_in_time_excds, icmp6stat.icp6s_inhist[ICMP6_TIME_EXCEEDED]); + rrddim_set_by_pointer(st, rd_in_parm_problems, icmp6stat.icp6s_inhist[ICMP6_PARAM_PROB]); + rrddim_set_by_pointer(st, rd_out_dest_unreachs, icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH]); + rrddim_set_by_pointer(st, rd_out_time_excds, icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED]); + rrddim_set_by_pointer(st, rd_out_parm_problems, icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_icmp6_echos == CONFIG_ONDEMAND_YES || (do_icmp6_echos == CONFIG_ONDEMAND_ONDEMAND && ( - icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST] || - icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST] || - icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY] || - icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]))) { - do_icmp6_echos = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmpechos"); - if (unlikely(!st)) { - st = rrdset_create("ipv6", "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE); + if (do_icmp6_echos == CONFIG_BOOLEAN_YES || (do_icmp6_echos == CONFIG_BOOLEAN_AUTO && ( + icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST] || + icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST] || + icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY] || + icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]))) { + do_icmp6_echos = CONFIG_BOOLEAN_YES; - 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); + static RRDSET *st = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL, *rd_in_replies = NULL, *rd_out_replies = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv6", + "icmpechos", + NULL, + "icmp", + NULL, + "IPv6 ICMP Echo", + "messages/s", + 10200, + update_every, + RRDSET_TYPE_LINE + ); + + rd_in = rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_replies = rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_replies = rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InEchos", icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST]); - rrddim_set(st, "OutEchos", icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST]); - rrddim_set(st, "InEchoReplies", icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY]); - rrddim_set(st, "OutEchoReplies", icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]); + rrddim_set_by_pointer(st, rd_in, icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST]); + rrddim_set_by_pointer(st, rd_out, icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST]); + rrddim_set_by_pointer(st, rd_in_replies, icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY]); + rrddim_set_by_pointer(st, rd_out_replies, icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_icmp6_router == CONFIG_ONDEMAND_YES || (do_icmp6_router == CONFIG_ONDEMAND_ONDEMAND && ( - icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT] || - icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT] || - icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT] || - icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]))) { - do_icmp6_router = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmprouter"); - if (unlikely(!st)) { - st = rrdset_create("ipv6", "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE); + if (do_icmp6_router == CONFIG_BOOLEAN_YES || (do_icmp6_router == CONFIG_BOOLEAN_AUTO && ( + icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT] || + icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT] || + icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT] || + icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]))) { + do_icmp6_router = CONFIG_BOOLEAN_YES; - 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); + static RRDSET *st = NULL; + static RRDDIM *rd_in_solicits = NULL, *rd_out_solicits = NULL, + *rd_in_advertisements = NULL, *rd_out_advertisements = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv6", + "icmprouter", + NULL, + "icmp", + NULL, + "IPv6 Router Messages", + "messages/s", + 10400, + update_every, + RRDSET_TYPE_LINE + ); + + rd_in_solicits = rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_solicits = rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_advertisements = rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_advertisements = rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InSolicits", icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT]); - rrddim_set(st, "OutSolicits", icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT]); - rrddim_set(st, "InAdvertisements", icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT]); - rrddim_set(st, "OutAdvertisements", icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]); + rrddim_set_by_pointer(st, rd_in_solicits, icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT]); + rrddim_set_by_pointer(st, rd_out_solicits, icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT]); + rrddim_set_by_pointer(st, rd_in_advertisements, icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT]); + rrddim_set_by_pointer(st, rd_out_advertisements, icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_icmp6_neighbor == CONFIG_ONDEMAND_YES || (do_icmp6_neighbor == CONFIG_ONDEMAND_ONDEMAND && ( - icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT] || - icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT] || - icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT] || - icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]))) { - do_icmp6_neighbor = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmpneighbor"); - if (unlikely(!st)) { - st = rrdset_create("ipv6", "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE); + if (do_icmp6_neighbor == CONFIG_BOOLEAN_YES || (do_icmp6_neighbor == CONFIG_BOOLEAN_AUTO && ( + icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT] || + icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT] || + icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT] || + icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]))) { + do_icmp6_neighbor = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_in_solicits = NULL, *rd_out_solicits = NULL, + *rd_in_advertisements = NULL, *rd_out_advertisements = NULL; - 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); + if (unlikely(!st)) { + st = rrdset_create_localhost("ipv6", + "icmpneighbor", + NULL, + "icmp", + NULL, + "IPv6 Neighbor Messages", + "messages/s", + 10500, + update_every, + RRDSET_TYPE_LINE + ); + + rd_in_solicits = rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_solicits = rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_advertisements = rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_advertisements = rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InSolicits", icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT]); - rrddim_set(st, "OutSolicits", icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]); - rrddim_set(st, "InAdvertisements", icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT]); - rrddim_set(st, "OutAdvertisements", icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]); + rrddim_set_by_pointer(st, rd_in_solicits, icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT]); + rrddim_set_by_pointer(st, rd_out_solicits, icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]); + rrddim_set_by_pointer(st, rd_in_advertisements, icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT]); + rrddim_set_by_pointer(st, rd_out_advertisements, icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]); rrdset_done(st); } // -------------------------------------------------------------------- - if (do_icmp6_types == CONFIG_ONDEMAND_YES || (do_icmp6_types == CONFIG_ONDEMAND_ONDEMAND && ( - icmp6stat.icp6s_inhist[1] || - icmp6stat.icp6s_inhist[128] || - icmp6stat.icp6s_inhist[129] || - icmp6stat.icp6s_inhist[136] || - icmp6stat.icp6s_outhist[1] || - icmp6stat.icp6s_outhist[128] || - icmp6stat.icp6s_outhist[129] || - icmp6stat.icp6s_outhist[133] || - icmp6stat.icp6s_outhist[135] || - icmp6stat.icp6s_outhist[136]))) { - do_icmp6_types = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmptypes"); + if (do_icmp6_types == CONFIG_BOOLEAN_YES || (do_icmp6_types == CONFIG_BOOLEAN_AUTO && ( + icmp6stat.icp6s_inhist[1] || + icmp6stat.icp6s_inhist[128] || + icmp6stat.icp6s_inhist[129] || + icmp6stat.icp6s_inhist[136] || + icmp6stat.icp6s_outhist[1] || + icmp6stat.icp6s_outhist[128] || + icmp6stat.icp6s_outhist[129] || + icmp6stat.icp6s_outhist[133] || + icmp6stat.icp6s_outhist[135] || + icmp6stat.icp6s_outhist[136]))) { + do_icmp6_types = CONFIG_BOOLEAN_YES; + + static RRDSET *st = NULL; + static RRDDIM *rd_in_1 = NULL, *rd_in_128 = NULL, *rd_in_129 = NULL, *rd_in_136 = NULL, + *rd_out_1 = NULL, *rd_out_128 = NULL, *rd_out_129 = NULL, *rd_out_133 = NULL, + *rd_out_135 = NULL, *rd_out_143 = NULL; + if (unlikely(!st)) { - st = rrdset_create("ipv6", "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); + st = rrdset_create_localhost("ipv6", + "icmptypes", + NULL, + "icmp", + NULL, + "IPv6 ICMP Types", + "messages/s", + 10700, + update_every, + RRDSET_TYPE_LINE + ); + + rd_in_1 = rrddim_add(st, "InType1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_128 = rrddim_add(st, "InType128", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_129 = rrddim_add(st, "InType129", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_in_136 = rrddim_add(st, "InType136", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_1 = rrddim_add(st, "OutType1", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_128 = rrddim_add(st, "OutType128", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_129 = rrddim_add(st, "OutType129", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_133 = rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_135 = rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out_143 = rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "InType1", icmp6stat.icp6s_inhist[1]); - rrddim_set(st, "InType128", icmp6stat.icp6s_inhist[128]); - rrddim_set(st, "InType129", icmp6stat.icp6s_inhist[129]); - rrddim_set(st, "InType136", icmp6stat.icp6s_inhist[136]); - rrddim_set(st, "OutType1", icmp6stat.icp6s_outhist[1]); - rrddim_set(st, "OutType128", icmp6stat.icp6s_outhist[128]); - rrddim_set(st, "OutType129", icmp6stat.icp6s_outhist[129]); - rrddim_set(st, "OutType133", icmp6stat.icp6s_outhist[133]); - rrddim_set(st, "OutType135", icmp6stat.icp6s_outhist[135]); - rrddim_set(st, "OutType143", icmp6stat.icp6s_outhist[143]); + rrddim_set_by_pointer(st, rd_in_1, icmp6stat.icp6s_inhist[1]); + rrddim_set_by_pointer(st, rd_in_128, icmp6stat.icp6s_inhist[128]); + rrddim_set_by_pointer(st, rd_in_129, icmp6stat.icp6s_inhist[129]); + rrddim_set_by_pointer(st, rd_in_136, icmp6stat.icp6s_inhist[136]); + rrddim_set_by_pointer(st, rd_out_1, icmp6stat.icp6s_outhist[1]); + rrddim_set_by_pointer(st, rd_out_128, icmp6stat.icp6s_outhist[128]); + rrddim_set_by_pointer(st, rd_out_129, icmp6stat.icp6s_outhist[129]); + rrddim_set_by_pointer(st, rd_out_133, icmp6stat.icp6s_outhist[133]); + rrddim_set_by_pointer(st, rd_out_135, icmp6stat.icp6s_outhist[135]); + rrddim_set_by_pointer(st, rd_out_143, icmp6stat.icp6s_outhist[143]); rrdset_done(st); } } + } else { + error("DISABLED: net.inet6.icmp6.stats module"); + return 1; } - // -------------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// getmntinfo + +int do_getmntinfo(int update_every, usec_t dt) { + (void)dt; + +#define DELAULT_EXLUDED_PATHS "/proc/*" +// taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes +#define DEFAULT_EXCLUDED_FILESYSTEMS "autofs procfs subfs devfs none" +#define CONFIG_SECTION_GETMNTINFO "plugin:freebsd:getmntinfo" + + static int do_space = -1, do_inodes = -1; + + if (unlikely(do_space == -1)) { + do_space = config_get_boolean_ondemand(CONFIG_SECTION_GETMNTINFO, "space usage for all disks", CONFIG_BOOLEAN_AUTO); + do_inodes = config_get_boolean_ondemand(CONFIG_SECTION_GETMNTINFO, "inodes usage for all disks", CONFIG_BOOLEAN_AUTO); + } if (likely(do_space || do_inodes)) { + struct statfs *mntbuf; + int mntsize; + // there is no mount info in sysctl MIBs if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) { error("FREEBSD: getmntinfo() failed"); do_space = 0; - error("DISABLED: disk_space.X"); + error("DISABLED: disk_space.* charts"); do_inodes = 0; - error("DISABLED: disk_inodes.X"); + error("DISABLED: disk_inodes.* charts"); + error("DISABLED: getmntinfo module"); + return 1; } else { + // Data to be stored in DICTIONARY mount_points. + // This DICTIONARY is used to lookup the settings of the mount point on each iteration. + struct mount_point_metadata { + int do_space; + int do_inodes; + + size_t collected; // the number of times this has been collected + + // charts and dimensions + + RRDSET *st_space; + RRDDIM *rd_space_used; + RRDDIM *rd_space_avail; + RRDDIM *rd_space_reserved; + + RRDSET *st_inodes; + RRDDIM *rd_inodes_used; + RRDDIM *rd_inodes_avail; + }; + static DICTIONARY *mount_points = NULL; + static SIMPLE_PATTERN *excluded_mountpoints = NULL; + static SIMPLE_PATTERN *excluded_filesystems = NULL; + int i; + + if(unlikely(!mount_points)) { + + excluded_mountpoints = simple_pattern_create( + config_get(CONFIG_SECTION_GETMNTINFO, "exclude space metrics on paths", + DELAULT_EXLUDED_PATHS), + SIMPLE_PATTERN_EXACT + ); + + excluded_filesystems = simple_pattern_create( + config_get(CONFIG_SECTION_GETMNTINFO, "exclude space metrics on filesystems", + DEFAULT_EXCLUDED_FILESYSTEMS), + SIMPLE_PATTERN_EXACT + ); + + mount_points = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + } + for (i = 0; i < mntsize; i++) { - if (mntbuf[i].f_flags == MNT_RDONLY || - mntbuf[i].f_blocks == 0 || - // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes - strcmp(mntbuf[i].f_fstypename, "autofs") == 0 || - strcmp(mntbuf[i].f_fstypename, "procfs") == 0 || - strcmp(mntbuf[i].f_fstypename, "subfs") == 0 || - strcmp(mntbuf[i].f_fstypename, "devfs") == 0 || - strcmp(mntbuf[i].f_fstypename, "none") == 0) + + char title[4096 + 1]; + int def_space, def_inodes, iter_space, iter_inodes; + + struct mount_point_metadata *m = dictionary_get(mount_points, mntbuf[i].f_mntonname); + if(unlikely(!m)) { + char var_name[4096 + 1]; + snprintfz(var_name, 4096, "%s:%s", CONFIG_SECTION_GETMNTINFO, mntbuf[i].f_mntonname); + + def_space = do_space; + def_inodes = do_space; + + if(unlikely(simple_pattern_matches(excluded_mountpoints, mntbuf[i].f_mntonname))) { + def_space = CONFIG_BOOLEAN_NO; + def_inodes = CONFIG_BOOLEAN_NO; + } + + if(unlikely(simple_pattern_matches(excluded_filesystems, mntbuf[i].f_fstypename))) { + def_space = CONFIG_BOOLEAN_NO; + def_inodes = CONFIG_BOOLEAN_NO; + } + + iter_space = config_get_boolean_ondemand(var_name, "space usage", def_space); + iter_inodes = config_get_boolean_ondemand(var_name, "inodes usage", def_inodes); + + struct mount_point_metadata mp = { + .do_space = iter_space, + .do_inodes = iter_inodes, + + .collected = 0, + + .st_space = NULL, + .rd_space_avail = NULL, + .rd_space_used = NULL, + .rd_space_reserved = NULL, + + .st_inodes = NULL, + .rd_inodes_avail = NULL, + .rd_inodes_used = NULL, + }; + + m = dictionary_set(mount_points, mntbuf[i].f_mntonname, &mp, sizeof(struct mount_point_metadata)); + } + + if(unlikely(m->do_space == CONFIG_BOOLEAN_NO && m->do_inodes == CONFIG_BOOLEAN_NO)) + continue; + + if(unlikely(mntbuf[i].f_flags & MNT_RDONLY && !m->collected)) continue; // -------------------------------------------------------------------------- - if (likely(do_space)) { - st = rrdset_find_bytype("disk_space", mntbuf[i].f_mntonname); - if (unlikely(!st)) { - snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); - st = rrdset_create("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023, - update_every, - RRDSET_TYPE_STACKED); - - rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE); - rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE); - rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR, - RRDDIM_ABSOLUTE); + int rendered = 0; + + if (m->do_space == CONFIG_BOOLEAN_YES || (m->do_space == CONFIG_BOOLEAN_AUTO && (mntbuf[i].f_blocks > 2))) { + if (unlikely(!m->st_space)) { + snprintfz(title, 4096, "Disk Space Usage for %s [%s]", + mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); + m->st_space = rrdset_create_localhost("disk_space", + mntbuf[i].f_mntonname, + NULL, + mntbuf[i].f_mntonname, + "disk.space", + title, + "GB", + 2023, + update_every, + RRDSET_TYPE_STACKED + ); + + m->rd_space_avail = rrddim_add(m->st_space, "avail", NULL, + mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); + m->rd_space_used = rrddim_add(m->st_space, "used", NULL, + mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); + m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root", + mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); } else - rrdset_next(st); + rrdset_next(m->st_space); - rrddim_set(st, "avail", (collected_number) mntbuf[i].f_bavail); - rrddim_set(st, "used", (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree)); - rrddim_set(st, "reserved_for_root", (collected_number) (mntbuf[i].f_bfree - mntbuf[i].f_bavail)); - rrdset_done(st); + rrddim_set_by_pointer(m->st_space, m->rd_space_avail, (collected_number) mntbuf[i].f_bavail); + rrddim_set_by_pointer(m->st_space, m->rd_space_used, (collected_number) (mntbuf[i].f_blocks - + mntbuf[i].f_bfree)); + rrddim_set_by_pointer(m->st_space, m->rd_space_reserved, (collected_number) (mntbuf[i].f_bfree - + mntbuf[i].f_bavail)); + rrdset_done(m->st_space); + + rendered++; } // -------------------------------------------------------------------------- - if (likely(do_inodes)) { - st = rrdset_find_bytype("disk_inodes", mntbuf[i].f_mntonname); - if (unlikely(!st)) { - snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); - st = rrdset_create("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "Inodes", 2024, - update_every, RRDSET_TYPE_STACKED); - - 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); + if (m->do_inodes == CONFIG_BOOLEAN_YES || (m->do_inodes == CONFIG_BOOLEAN_AUTO && (mntbuf[i].f_files > 1))) { + if (unlikely(!m->st_inodes)) { + snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", + mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); + m->st_inodes = rrdset_create_localhost("disk_inodes", + mntbuf[i].f_mntonname, + NULL, + mntbuf[i].f_mntonname, + "disk.inodes", + title, + "Inodes", + 2024, + update_every, + RRDSET_TYPE_STACKED + ); + + m->rd_inodes_avail = rrddim_add(m->st_inodes, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + m->rd_inodes_used = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else - rrdset_next(st); + rrdset_next(m->st_inodes); - rrddim_set(st, "avail", (collected_number) mntbuf[i].f_ffree); - rrddim_set(st, "used", (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree)); - rrdset_done(st); + rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_avail, (collected_number) mntbuf[i].f_ffree); + rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_used, (collected_number) (mntbuf[i].f_files - + mntbuf[i].f_ffree)); + rrdset_done(m->st_inodes); + + rendered++; } + + if(likely(rendered)) + m->collected++; } } + } else { + error("DISABLED: getmntinfo module"); + return 1; } - // -------------------------------------------------------------------- + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// getifaddrs - if (likely(do_uptime)) { - if (unlikely(GETSYSCTL("kern.boottime", boot_time))) { - do_uptime = 0; - error("DISABLED: system.uptime"); +int do_getifaddrs(int update_every, usec_t dt) { + (void)dt; + +#define DELAULT_EXLUDED_INTERFACES "lo*" +#define CONFIG_SECTION_GETIFADDRS "plugin:freebsd:getifaddrs" + + static int do_bandwidth_ipv4 = -1, do_bandwidth_ipv6 = -1, do_bandwidth = -1, do_packets = -1, + do_errors = -1, do_drops = -1, do_events = -1; + + if (unlikely(do_bandwidth_ipv4 == -1)) { + do_bandwidth_ipv4 = config_get_boolean_ondemand(CONFIG_SECTION_GETIFADDRS, "total bandwidth for ipv4 interfaces", + CONFIG_BOOLEAN_AUTO); + do_bandwidth_ipv6 = config_get_boolean_ondemand(CONFIG_SECTION_GETIFADDRS, "total bandwidth for ipv6 interfaces", + CONFIG_BOOLEAN_AUTO); + do_bandwidth = config_get_boolean_ondemand(CONFIG_SECTION_GETIFADDRS, "bandwidth for all interfaces", + CONFIG_BOOLEAN_AUTO); + do_packets = config_get_boolean_ondemand(CONFIG_SECTION_GETIFADDRS, "packets for all interfaces", + CONFIG_BOOLEAN_AUTO); + do_errors = config_get_boolean_ondemand(CONFIG_SECTION_GETIFADDRS, "errors for all interfaces", + CONFIG_BOOLEAN_AUTO); + do_drops = config_get_boolean_ondemand(CONFIG_SECTION_GETIFADDRS, "drops for all interfaces", + CONFIG_BOOLEAN_AUTO); + do_events = config_get_boolean_ondemand(CONFIG_SECTION_GETIFADDRS, "collisions for all interfaces", + CONFIG_BOOLEAN_AUTO); + } + + if (likely(do_bandwidth_ipv4 || do_bandwidth_ipv6 || do_bandwidth || do_packets || do_errors || + do_drops || do_events)) { + struct ifaddrs *ifap; + + if (unlikely(getifaddrs(&ifap))) { + error("FREEBSD: getifaddrs() failed"); + do_bandwidth_ipv4 = 0; + error("DISABLED: system.ipv4 chart"); + do_bandwidth_ipv6 = 0; + error("DISABLED: system.ipv6 chart"); + do_bandwidth = 0; + error("DISABLED: net.* charts"); + do_packets = 0; + error("DISABLED: net_packets.* charts"); + do_errors = 0; + error("DISABLED: net_errors.* charts"); + do_drops = 0; + error("DISABLED: net_drops.* charts"); + do_events = 0; + error("DISABLED: net_events.* charts"); + error("DISABLED: getifaddrs module"); + return 1; } else { - clock_gettime(CLOCK_REALTIME, &cur_time); - st = rrdset_find("system.uptime"); + #define IFA_DATA(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s) + struct ifaddrs *ifa; + struct iftot { + u_long ift_ibytes; + u_long ift_obytes; + } iftot = {0, 0}; - if(unlikely(!st)) { - st = rrdset_create("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "uptime", NULL, 1, 1, RRDDIM_ABSOLUTE); + // -------------------------------------------------------------------- + + if (likely(do_bandwidth_ipv4)) { + iftot.ift_ibytes = iftot.ift_obytes = 0; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + iftot.ift_ibytes += IFA_DATA(ibytes); + iftot.ift_obytes += IFA_DATA(obytes); + } + + static RRDSET *st = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "ipv4", + NULL, + "network", + NULL, + "IPv4 Bandwidth", + "kilobits/s", + 500, + update_every, + RRDSET_TYPE_AREA + ); + + rd_in = rrddim_add(st, "InOctets", "received", 8, KILO_FACTOR, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st, "OutOctets", "sent", -8, KILO_FACTOR, RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set_by_pointer(st, rd_in, iftot.ift_ibytes); + rrddim_set_by_pointer(st, rd_out, iftot.ift_obytes); + rrdset_done(st); } - else rrdset_next(st); - rrddim_set(st, "uptime", cur_time.tv_sec - boot_time.tv_sec); - rrdset_done(st); + // -------------------------------------------------------------------- + + if (likely(do_bandwidth_ipv6)) { + iftot.ift_ibytes = iftot.ift_obytes = 0; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + iftot.ift_ibytes += IFA_DATA(ibytes); + iftot.ift_obytes += IFA_DATA(obytes); + } + + static RRDSET *st = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "ipv6", + NULL, + "network", + NULL, + "IPv6 Bandwidth", + "kilobits/s", + 500, + update_every, + RRDSET_TYPE_AREA + ); + + rd_in = rrddim_add(st, "received", NULL, 8, KILO_FACTOR, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st, "sent", NULL, -8, KILO_FACTOR, RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set_by_pointer(st, rd_in, iftot.ift_ibytes); + rrddim_set_by_pointer(st, rd_out, iftot.ift_obytes); + rrdset_done(st); + } + + // -------------------------------------------------------------------- + + // Data to be stored in DICTIONARY interfaces. + // This DICTIONARY is used to lookup the settings of the interfaces on each iteration. + struct interfaces_metadata { + int do_bandwidth; + int do_packets; + int do_errors; + int do_drops; + int do_events; + + // charts and dimensions + + RRDSET *st_bandwidth; + RRDDIM *rd_bandwidth_in; + RRDDIM *rd_bandwidth_out; + + RRDSET *st_packets; + RRDDIM *rd_packets_in; + RRDDIM *rd_packets_out; + RRDDIM *rd_packets_m_in; + RRDDIM *rd_packets_m_out; + + RRDSET *st_errors; + RRDDIM *rd_errors_in; + RRDDIM *rd_errors_out; + + RRDSET *st_drops; + RRDDIM *rd_drops_in; + RRDDIM *rd_drops_out; + + RRDSET *st_events; + RRDDIM *rd_events_coll; + }; + static DICTIONARY *interfaces = NULL; + static SIMPLE_PATTERN *excluded_interfaces = NULL; + + if(unlikely(!interfaces)) { + + excluded_interfaces = simple_pattern_create( + config_get(CONFIG_SECTION_GETIFADDRS, "disable by default interfaces matching", + DELAULT_EXLUDED_INTERFACES) + , SIMPLE_PATTERN_EXACT + ); + + interfaces = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + int def_bandwidth, def_packets, def_errors, def_drops, def_events, + iter_bandwidth, iter_packets, iter_errors, iter_drops, iter_events; + + struct interfaces_metadata *ifm = dictionary_get(interfaces, ifa->ifa_name); + if(unlikely(!ifm)) { + char var_name[4096 + 1]; + snprintfz(var_name, 4096, "%s:%s", CONFIG_SECTION_GETIFADDRS, ifa->ifa_name); + + def_bandwidth = do_bandwidth; + def_packets = do_packets; + def_errors = do_errors; + def_drops = do_drops; + def_events = do_events; + + if(unlikely(simple_pattern_matches(excluded_interfaces, ifa->ifa_name))) { + def_bandwidth = CONFIG_BOOLEAN_NO; + def_packets = CONFIG_BOOLEAN_NO; + def_errors = CONFIG_BOOLEAN_NO; + def_drops = CONFIG_BOOLEAN_NO; + def_events = CONFIG_BOOLEAN_NO; + } + + iter_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", def_bandwidth); + iter_packets = config_get_boolean_ondemand(var_name, "packets", def_packets); + iter_errors = config_get_boolean_ondemand(var_name, "errors", def_errors); + iter_drops = config_get_boolean_ondemand(var_name, "drops", def_drops); + iter_events = config_get_boolean_ondemand(var_name, "events", def_events); + + struct interfaces_metadata ifmp = { + .do_bandwidth = iter_bandwidth, + .do_packets = iter_packets, + .do_errors = iter_errors, + .do_drops = iter_drops, + .do_events = iter_events, + + .st_bandwidth = NULL, + .rd_bandwidth_in = NULL, + .rd_bandwidth_out = NULL, + + .st_packets = NULL, + .rd_packets_in = NULL, + .rd_packets_out = NULL, + .rd_packets_m_in = NULL, + .rd_packets_m_out = NULL, + + .st_errors = NULL, + .rd_errors_in = NULL, + .rd_errors_out = NULL, + + .st_drops = NULL, + .rd_drops_in = NULL, + .rd_drops_out = NULL, + + .st_events = NULL, + .rd_events_coll = NULL, + }; + + ifm = dictionary_set(interfaces, ifa->ifa_name, &ifmp, sizeof(struct interfaces_metadata)); + } + + // -------------------------------------------------------------------- + + if (ifm->do_bandwidth == CONFIG_BOOLEAN_YES || (ifm->do_bandwidth == CONFIG_BOOLEAN_AUTO && + (IFA_DATA(ibytes) || IFA_DATA(obytes)))) { + if (unlikely(!ifm->st_bandwidth)) { + ifm->st_bandwidth = rrdset_create_localhost("net", + ifa->ifa_name, + NULL, + ifa->ifa_name, + "net.net", + "Bandwidth", + "kilobits/s", + 7000, + update_every, + RRDSET_TYPE_AREA + ); + + ifm->rd_bandwidth_in = rrddim_add(ifm->st_bandwidth, "received", NULL, 8, KILO_FACTOR, + RRD_ALGORITHM_INCREMENTAL); + ifm->rd_bandwidth_out = rrddim_add(ifm->st_bandwidth, "sent", NULL, -8, KILO_FACTOR, + RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(ifm->st_bandwidth); + + rrddim_set_by_pointer(ifm->st_bandwidth, ifm->rd_bandwidth_in, IFA_DATA(ibytes)); + rrddim_set_by_pointer(ifm->st_bandwidth, ifm->rd_bandwidth_out, IFA_DATA(obytes)); + rrdset_done(ifm->st_bandwidth); + } + + // -------------------------------------------------------------------- + + if (ifm->do_packets == CONFIG_BOOLEAN_YES || (ifm->do_packets == CONFIG_BOOLEAN_AUTO && + (IFA_DATA(ipackets) || IFA_DATA(opackets) || IFA_DATA(imcasts) || IFA_DATA(omcasts)))) { + if (unlikely(!ifm->st_packets)) { + ifm->st_packets = rrdset_create_localhost("net_packets", + ifa->ifa_name, + NULL, + ifa->ifa_name, + "net.packets", + "Packets", + "packets/s", + 7001, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(ifm->st_packets, RRDSET_FLAG_DETAIL); + + ifm->rd_packets_in = rrddim_add(ifm->st_packets, "received", NULL, 1, 1, + RRD_ALGORITHM_INCREMENTAL); + ifm->rd_packets_out = rrddim_add(ifm->st_packets, "sent", NULL, -1, 1, + RRD_ALGORITHM_INCREMENTAL); + ifm->rd_packets_m_in = rrddim_add(ifm->st_packets, "multicast_received", NULL, 1, 1, + RRD_ALGORITHM_INCREMENTAL); + ifm->rd_packets_m_out = rrddim_add(ifm->st_packets, "multicast_sent", NULL, -1, 1, + RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(ifm->st_packets); + + rrddim_set_by_pointer(ifm->st_packets, ifm->rd_packets_in, IFA_DATA(ipackets)); + rrddim_set_by_pointer(ifm->st_packets, ifm->rd_packets_out, IFA_DATA(opackets)); + rrddim_set_by_pointer(ifm->st_packets, ifm->rd_packets_m_in, IFA_DATA(imcasts)); + rrddim_set_by_pointer(ifm->st_packets, ifm->rd_packets_m_out, IFA_DATA(omcasts)); + rrdset_done(ifm->st_packets); + } + + // -------------------------------------------------------------------- + + if (ifm->do_errors == CONFIG_BOOLEAN_YES || (ifm->do_errors == CONFIG_BOOLEAN_AUTO && + (IFA_DATA(ierrors) || IFA_DATA(oerrors)))) { + if (unlikely(!ifm->st_errors)) { + ifm->st_errors = rrdset_create_localhost("net_errors", + ifa->ifa_name, + NULL, + ifa->ifa_name, + "net.errors", + "Interface Errors", + "errors/s", + 7002, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(ifm->st_errors, RRDSET_FLAG_DETAIL); + + ifm->rd_errors_in = rrddim_add(ifm->st_errors, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + ifm->rd_errors_out = rrddim_add(ifm->st_errors, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(ifm->st_errors); + + rrddim_set_by_pointer(ifm->st_errors, ifm->rd_errors_in, IFA_DATA(ierrors)); + rrddim_set_by_pointer(ifm->st_errors, ifm->rd_errors_out, IFA_DATA(oerrors)); + rrdset_done(ifm->st_errors); + } + // -------------------------------------------------------------------- + + if (ifm->do_drops == CONFIG_BOOLEAN_YES || (ifm->do_drops == CONFIG_BOOLEAN_AUTO && + (IFA_DATA(iqdrops) || IFA_DATA(oqdrops)))) { + if (unlikely(!ifm->st_drops)) { + ifm->st_drops = rrdset_create_localhost("net_drops", + ifa->ifa_name, + NULL, + ifa->ifa_name, + "net.drops", + "Interface Drops", + "drops/s", + 7003, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(ifm->st_drops, RRDSET_FLAG_DETAIL); + + ifm->rd_drops_in = rrddim_add(ifm->st_drops, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); +#if __FreeBSD__ >= 11 + ifm->rd_drops_out = rrddim_add(ifm->st_drops, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); +#endif + } else + rrdset_next(ifm->st_drops); + + rrddim_set_by_pointer(ifm->st_drops, ifm->rd_drops_in, IFA_DATA(iqdrops)); +#if __FreeBSD__ >= 11 + rrddim_set_by_pointer(ifm->st_drops, ifm->rd_drops_out, IFA_DATA(oqdrops)); +#endif + rrdset_done(ifm->st_drops); + } + + // -------------------------------------------------------------------- + + if (ifm->do_events == CONFIG_BOOLEAN_YES || (ifm->do_events == CONFIG_BOOLEAN_AUTO && + IFA_DATA(collisions))) { + if (unlikely(!ifm->st_events)) { + ifm->st_events = rrdset_create_localhost("net_events", + ifa->ifa_name, + NULL, + ifa->ifa_name, + "net.events", + "Network Interface Events", + "events/s", + 7006, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(ifm->st_events, RRDSET_FLAG_DETAIL); + + ifm->rd_events_coll = rrddim_add(ifm->st_events, "collisions", NULL, -1, 1, + RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(ifm->st_events); + + rrddim_set_by_pointer(ifm->st_events, ifm->rd_events_coll, IFA_DATA(collisions)); + rrdset_done(ifm->st_events); + } + } + + freeifaddrs(ifap); + } + } else { + error("DISABLED: getifaddrs module"); + return 1; + } + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------- +// kern.devstat + +int do_kern_devstat(int update_every, usec_t dt) { + +#define DELAULT_EXLUDED_DISKS "" +#define CONFIG_SECTION_KERN_DEVSTAT "plugin:freebsd:kern.devstat" +#define BINTIME_SCALE 5.42101086242752217003726400434970855712890625e-17 // this is 1000/2^64 + + static int enable_pass_devices = -1, do_system_io = -1, do_io = -1, do_ops = -1, do_qops = -1, do_util = -1, + do_iotime = -1, do_await = -1, do_avagsz = -1, do_svctm = -1; + + if (unlikely(enable_pass_devices == -1)) { + enable_pass_devices = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, + "performance metrics for pass devices", CONFIG_BOOLEAN_AUTO); + + do_system_io = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, "total bandwidth for all disks", + CONFIG_BOOLEAN_YES); + + do_io = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, "bandwidth for all disks", + CONFIG_BOOLEAN_AUTO); + do_ops = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, "operations for all disks", + CONFIG_BOOLEAN_AUTO); + do_qops = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, "queued operations for all disks", + CONFIG_BOOLEAN_AUTO); + do_util = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, "utilization percentage for all disks", + CONFIG_BOOLEAN_AUTO); + do_iotime = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, "i/o time for all disks", + CONFIG_BOOLEAN_AUTO); + do_await = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, "average completed i/o time for all disks", + CONFIG_BOOLEAN_AUTO); + do_avagsz = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, "average completed i/o bandwidth for all disks", + CONFIG_BOOLEAN_AUTO); + do_svctm = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, "average service time for all disks", + CONFIG_BOOLEAN_AUTO); + } + + if (likely(do_system_io || do_io || do_ops || do_qops || do_util || do_iotime || do_await || do_avagsz || do_svctm)) { + static int mib_numdevs[3] = {0, 0, 0}; + int numdevs; + int common_error = 0; + + if (unlikely(GETSYSCTL_SIMPLE("kern.devstat.numdevs", mib_numdevs, numdevs))) { + common_error = 1; + } else { + static int mib_devstat[3] = {0, 0, 0}; + static void *devstat_data = NULL; + + devstat_data = reallocz(devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs); // there is generation number before devstat structures + if (unlikely(GETSYSCTL_WSIZE("kern.devstat.all", mib_devstat, devstat_data, + sizeof(long) + sizeof(struct devstat) * numdevs))) { + common_error = 1; + } else { + struct devstat *dstat; + int i; + collected_number total_disk_kbytes_read = 0; + collected_number total_disk_kbytes_write = 0; + + // Data to be stored in DICTIONARY disks. + // This DICTIONARY is used to lookup the settings of the disks on each iteration. + struct disks_metadata { + int do_io; + int do_ops; + int do_qops; + int do_util; + int do_iotime; + int do_await; + int do_avagsz; + int do_svctm; + + + // data for differential charts + + struct prev_dstat { + collected_number bytes_read; + collected_number bytes_write; + collected_number operations_read; + collected_number operations_write; + collected_number duration_read_ms; + collected_number duration_write_ms; + collected_number busy_time_ms; + } prev_dstat; + + // charts and dimensions + + RRDSET *st_io; + RRDDIM *rd_io_in; + RRDDIM *rd_io_out; + + RRDSET *st_ops; + RRDDIM *rd_ops_in; + RRDDIM *rd_ops_out; + + RRDSET *st_qops; + RRDDIM *rd_qops; + + RRDSET *st_util; + RRDDIM *rd_util; + + RRDSET *st_iotime; + RRDDIM *rd_iotime_in; + RRDDIM *rd_iotime_out; + + RRDSET *st_await; + RRDDIM *rd_await_in; + RRDDIM *rd_await_out; + + RRDSET *st_avagsz; + RRDDIM *rd_avagsz_in; + RRDDIM *rd_avagsz_out; + + RRDSET *st_svctm; + RRDDIM *rd_svctm; + + }; + static DICTIONARY *disks = NULL; + static SIMPLE_PATTERN *excluded_disks = NULL; + + if(unlikely(!disks)) { + + excluded_disks = simple_pattern_create( + config_get(CONFIG_SECTION_KERN_DEVSTAT, "disable by default disks matching", + DELAULT_EXLUDED_DISKS) + , SIMPLE_PATTERN_EXACT + ); + + disks = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); + } + + dstat = devstat_data + sizeof(long); // skip generation number + + for (i = 0; i < numdevs; i++) { + if (likely(do_system_io)) { + if (((dstat[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) || ((dstat[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_STORARRAY)) { + total_disk_kbytes_read += dstat[i].bytes[DEVSTAT_READ] / KILO_FACTOR; + total_disk_kbytes_write += dstat[i].bytes[DEVSTAT_WRITE] / KILO_FACTOR; + } + } + + if (unlikely(!enable_pass_devices)) + if ((dstat[i].device_type & DEVSTAT_TYPE_PASS) == DEVSTAT_TYPE_PASS) + continue; + + if (((dstat[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) || ((dstat[i].device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_STORARRAY)) { + char disk[DEVSTAT_NAME_LEN + MAX_INT_DIGITS + 1]; + int def_io, def_ops, def_qops, def_util, def_iotime, def_await, def_avagsz, def_svctm, + iter_io, iter_ops, iter_qops, iter_util, iter_iotime, iter_await, iter_avagsz, iter_svctm; + struct cur_dstat { + collected_number duration_read_ms; + collected_number duration_write_ms; + collected_number busy_time_ms; + } cur_dstat; + + sprintf(disk, "%s%d", dstat[i].device_name, dstat[i].unit_number); + + struct disks_metadata *dm = dictionary_get(disks, disk); + if(unlikely(!dm)) { + char var_name[4096 + 1]; + snprintfz(var_name, 4096, "%s:%s", CONFIG_SECTION_KERN_DEVSTAT, disk); + + def_io = do_io; + def_ops = do_ops; + def_qops = do_qops; + def_util = do_util; + def_iotime = do_iotime; + def_await = do_await; + def_avagsz = do_avagsz; + def_svctm = do_svctm; + + if(unlikely(simple_pattern_matches(excluded_disks, disk))) { + def_io = CONFIG_BOOLEAN_NO; + def_ops = CONFIG_BOOLEAN_NO; + def_qops = CONFIG_BOOLEAN_NO; + def_util = CONFIG_BOOLEAN_NO; + def_iotime = CONFIG_BOOLEAN_NO; + def_await = CONFIG_BOOLEAN_NO; + def_avagsz = CONFIG_BOOLEAN_NO; + def_svctm = CONFIG_BOOLEAN_NO; + } + + iter_io = config_get_boolean_ondemand(var_name, "bandwidth", def_io); + iter_ops = config_get_boolean_ondemand(var_name, "operations", def_ops); + iter_qops = config_get_boolean_ondemand(var_name, "queued operations", def_qops); + iter_util = config_get_boolean_ondemand(var_name, "utilization percentage", def_util); + iter_iotime = config_get_boolean_ondemand(var_name, "i/o time", def_iotime); + iter_await = config_get_boolean_ondemand(var_name, "average completed i/o time", def_await); + iter_avagsz = config_get_boolean_ondemand(var_name, "average completed i/o bandwidth", + def_avagsz); + iter_svctm = config_get_boolean_ondemand(var_name, "average service time", def_svctm); + + struct disks_metadata dmp = { + .do_io = iter_io, + .do_ops = iter_ops, + .do_qops = iter_qops, + .do_util = iter_util, + .do_iotime = iter_iotime, + .do_await = iter_await, + .do_avagsz = iter_avagsz, + .do_svctm = iter_svctm, + + .st_io = NULL, + .rd_io_in = NULL, + .rd_io_out = NULL, + + .st_ops = NULL, + .rd_ops_in = NULL, + .rd_ops_out = NULL, + + .st_qops = NULL, + .rd_qops = NULL, + + .st_util = NULL, + .rd_util = NULL, + + .st_iotime = NULL, + .rd_iotime_in = NULL, + .rd_iotime_out = NULL, + + .st_await = NULL, + .rd_await_in = NULL, + .rd_await_out = NULL, + + .st_avagsz = NULL, + .rd_avagsz_in = NULL, + .rd_avagsz_out = NULL, + + .st_svctm = NULL, + .rd_svctm = NULL, + }; + + // initialise data for differential charts + + dmp.prev_dstat.bytes_read = dstat[i].bytes[DEVSTAT_READ]; + dmp.prev_dstat.bytes_write = dstat[i].bytes[DEVSTAT_WRITE]; + dmp.prev_dstat.operations_read = dstat[i].operations[DEVSTAT_READ]; + dmp.prev_dstat.operations_write = dstat[i].operations[DEVSTAT_WRITE]; + dmp.prev_dstat.duration_read_ms = dstat[i].duration[DEVSTAT_READ].sec * 1000 + + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; + dmp.prev_dstat.duration_write_ms = dstat[i].duration[DEVSTAT_WRITE].sec * 1000 + + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; + dmp.prev_dstat.busy_time_ms = dstat[i].busy_time.sec * 1000 + + dstat[i].busy_time.frac * BINTIME_SCALE; + + dm = dictionary_set(disks, disk, &dmp, sizeof(struct disks_metadata)); + } + + cur_dstat.duration_read_ms = dstat[i].duration[DEVSTAT_READ].sec * 1000 + + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; + cur_dstat.duration_write_ms = dstat[i].duration[DEVSTAT_WRITE].sec * 1000 + + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; + cur_dstat.busy_time_ms = dstat[i].busy_time.sec * 1000 + dstat[i].busy_time.frac * BINTIME_SCALE; + + // -------------------------------------------------------------------- + + if(dm->do_io == CONFIG_BOOLEAN_YES || (dm->do_io == CONFIG_BOOLEAN_AUTO && + (dstat[i].bytes[DEVSTAT_READ] || dstat[i].bytes[DEVSTAT_WRITE]))) { + if (unlikely(!dm->st_io)) { + dm->st_io = rrdset_create_localhost("disk", + disk, + NULL, + disk, + "disk.io", + "Disk I/O Bandwidth", + "kilobytes/s", + 2000, + update_every, + RRDSET_TYPE_AREA + ); + + dm->rd_io_in = rrddim_add(dm->st_io, "reads", NULL, 1, KILO_FACTOR, + RRD_ALGORITHM_INCREMENTAL); + dm->rd_io_out = rrddim_add(dm->st_io, "writes", NULL, -1, KILO_FACTOR, + RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(dm->st_io); + + rrddim_set_by_pointer(dm->st_io, dm->rd_io_in, dstat[i].bytes[DEVSTAT_READ]); + rrddim_set_by_pointer(dm->st_io, dm->rd_io_out, dstat[i].bytes[DEVSTAT_WRITE]); + rrdset_done(dm->st_io); + } + + // -------------------------------------------------------------------- + + if(dm->do_ops == CONFIG_BOOLEAN_YES || (dm->do_ops == CONFIG_BOOLEAN_AUTO && + (dstat[i].operations[DEVSTAT_READ] || dstat[i].operations[DEVSTAT_WRITE]))) { + if (unlikely(!dm->st_ops)) { + dm->st_ops = rrdset_create_localhost("disk_ops", + disk, + NULL, + disk, + "disk.ops", + "Disk Completed I/O Operations", + "operations/s", + 2001, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(dm->st_ops, RRDSET_FLAG_DETAIL); + + dm->rd_ops_in = rrddim_add(dm->st_ops, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + dm->rd_ops_out = rrddim_add(dm->st_ops, "writes", NULL, -1, 1, + RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(dm->st_ops); + + rrddim_set_by_pointer(dm->st_ops, dm->rd_ops_in, dstat[i].operations[DEVSTAT_READ]); + rrddim_set_by_pointer(dm->st_ops, dm->rd_ops_out, dstat[i].operations[DEVSTAT_WRITE]); + rrdset_done(dm->st_ops); + } + + // -------------------------------------------------------------------- + + if(dm->do_qops == CONFIG_BOOLEAN_YES || (dm->do_qops == CONFIG_BOOLEAN_AUTO && + (dstat[i].start_count || dstat[i].end_count))) { + if (unlikely(!dm->st_qops)) { + dm->st_qops = rrdset_create_localhost("disk_qops", + disk, + NULL, + disk, + "disk.qops", + "Disk Current I/O Operations", + "operations", + 2002, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(dm->st_qops, RRDSET_FLAG_DETAIL); + + dm->rd_qops = rrddim_add(dm->st_qops, "operations", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } else + rrdset_next(dm->st_qops); + + rrddim_set_by_pointer(dm->st_qops, dm->rd_qops, dstat[i].start_count - dstat[i].end_count); + rrdset_done(dm->st_qops); + } + + // -------------------------------------------------------------------- + + if(dm->do_util == CONFIG_BOOLEAN_YES || (dm->do_util == CONFIG_BOOLEAN_AUTO && + cur_dstat.busy_time_ms)) { + if (unlikely(!dm->st_util)) { + dm->st_util = rrdset_create_localhost("disk_util", + disk, + NULL, + disk, + "disk.util", + "Disk Utilization Time", + "% of time working", + 2004, + update_every, + RRDSET_TYPE_AREA + ); + + rrdset_flag_set(dm->st_util, RRDSET_FLAG_DETAIL); + + dm->rd_util = rrddim_add(dm->st_util, "utilization", NULL, 1, 10, + RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(dm->st_util); + + rrddim_set_by_pointer(dm->st_util, dm->rd_util, cur_dstat.busy_time_ms); + rrdset_done(dm->st_util); + } + + // -------------------------------------------------------------------- + + if(dm->do_iotime == CONFIG_BOOLEAN_YES || (dm->do_iotime == CONFIG_BOOLEAN_AUTO && + (cur_dstat.duration_read_ms || cur_dstat.duration_write_ms))) { + if (unlikely(!dm->st_iotime)) { + dm->st_iotime = rrdset_create_localhost("disk_iotime", + disk, + NULL, + disk, + "disk.iotime", + "Disk Total I/O Time", + "milliseconds/s", + 2022, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(dm->st_iotime, RRDSET_FLAG_DETAIL); + + dm->rd_iotime_in = rrddim_add(dm->st_iotime, "reads", NULL, 1, 1, + RRD_ALGORITHM_INCREMENTAL); + dm->rd_iotime_out = rrddim_add(dm->st_iotime, "writes", NULL, -1, 1, + RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(dm->st_iotime); + + rrddim_set_by_pointer(dm->st_iotime, dm->rd_iotime_in, cur_dstat.duration_read_ms); + rrddim_set_by_pointer(dm->st_iotime, dm->rd_iotime_out, cur_dstat.duration_write_ms); + rrdset_done(dm->st_iotime); + } + + // -------------------------------------------------------------------- + // calculate differential charts + // only if this is not the first time we run + + if (likely(dt)) { + + // -------------------------------------------------------------------- + + if(dm->do_await == CONFIG_BOOLEAN_YES || (dm->do_await == CONFIG_BOOLEAN_AUTO && + (dstat[i].operations[DEVSTAT_READ] || dstat[i].operations[DEVSTAT_WRITE]))) { + if (unlikely(!dm->st_await)) { + dm->st_await = rrdset_create_localhost("disk_await", + disk, + NULL, + disk, + "disk.await", + "Average Completed I/O Operation Time", + "ms per operation", + 2005, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(dm->st_await, RRDSET_FLAG_DETAIL); + + dm->rd_await_in = rrddim_add(dm->st_await, "reads", NULL, 1, 1, + RRD_ALGORITHM_ABSOLUTE); + dm->rd_await_out = rrddim_add(dm->st_await, "writes", NULL, -1, 1, + RRD_ALGORITHM_ABSOLUTE); + } else + rrdset_next(dm->st_await); + + rrddim_set_by_pointer(dm->st_await, dm->rd_await_in, + (dstat[i].operations[DEVSTAT_READ] - + dm->prev_dstat.operations_read) ? + (cur_dstat.duration_read_ms - dm->prev_dstat.duration_read_ms) / + (dstat[i].operations[DEVSTAT_READ] - + dm->prev_dstat.operations_read) : + 0); + rrddim_set_by_pointer(dm->st_await, dm->rd_await_out, + (dstat[i].operations[DEVSTAT_WRITE] - + dm->prev_dstat.operations_write) ? + (cur_dstat.duration_write_ms - dm->prev_dstat.duration_write_ms) / + (dstat[i].operations[DEVSTAT_WRITE] - + dm->prev_dstat.operations_write) : + 0); + rrdset_done(dm->st_await); + } + + // -------------------------------------------------------------------- + + if(dm->do_avagsz == CONFIG_BOOLEAN_YES || (dm->do_avagsz == CONFIG_BOOLEAN_AUTO && + (dstat[i].operations[DEVSTAT_READ] || dstat[i].operations[DEVSTAT_WRITE]))) { + if (unlikely(!dm->st_avagsz)) { + dm->st_avagsz = rrdset_create_localhost("disk_avgsz", + disk, + NULL, + disk, + "disk.avgsz", + "Average Completed I/O Operation Bandwidth", + "kilobytes per operation", + 2006, + update_every, + RRDSET_TYPE_AREA + ); + + rrdset_flag_set(dm->st_avagsz, RRDSET_FLAG_DETAIL); + + dm->rd_avagsz_in = rrddim_add(dm->st_avagsz, "reads", NULL, 1, KILO_FACTOR, + RRD_ALGORITHM_ABSOLUTE); + dm->rd_avagsz_out = rrddim_add(dm->st_avagsz, "writes", NULL, -1, KILO_FACTOR, + RRD_ALGORITHM_ABSOLUTE); + } else + rrdset_next(dm->st_avagsz); + + rrddim_set_by_pointer(dm->st_avagsz, dm->rd_avagsz_in, + (dstat[i].operations[DEVSTAT_READ] - + dm->prev_dstat.operations_read) ? + (dstat[i].bytes[DEVSTAT_READ] - dm->prev_dstat.bytes_read) / + (dstat[i].operations[DEVSTAT_READ] - + dm->prev_dstat.operations_read) : + 0); + rrddim_set_by_pointer(dm->st_avagsz, dm->rd_avagsz_out, + (dstat[i].operations[DEVSTAT_WRITE] - + dm->prev_dstat.operations_write) ? + (dstat[i].bytes[DEVSTAT_WRITE] - dm->prev_dstat.bytes_write) / + (dstat[i].operations[DEVSTAT_WRITE] - + dm->prev_dstat.operations_write) : + 0); + rrdset_done(dm->st_avagsz); + } + + // -------------------------------------------------------------------- + + if(dm->do_svctm == CONFIG_BOOLEAN_YES || (dm->do_svctm == CONFIG_BOOLEAN_AUTO && + (dstat[i].operations[DEVSTAT_READ] || dstat[i].operations[DEVSTAT_WRITE]))) { + if (unlikely(!dm->st_svctm)) { + dm->st_svctm = rrdset_create_localhost("disk_svctm", + disk, + NULL, + disk, + "disk.svctm", + "Average Service Time", + "ms per operation", + 2007, + update_every, + RRDSET_TYPE_LINE + ); + + rrdset_flag_set(dm->st_svctm, RRDSET_FLAG_DETAIL); + + dm->rd_svctm = rrddim_add(dm->st_svctm, "svctm", NULL, 1, 1, + RRD_ALGORITHM_ABSOLUTE); + } else + rrdset_next(dm->st_svctm); + + rrddim_set_by_pointer(dm->st_svctm, dm->rd_svctm, + ((dstat[i].operations[DEVSTAT_READ] - dm->prev_dstat.operations_read) + + (dstat[i].operations[DEVSTAT_WRITE] - dm->prev_dstat.operations_write)) ? + (cur_dstat.busy_time_ms - dm->prev_dstat.busy_time_ms) / + ((dstat[i].operations[DEVSTAT_READ] - dm->prev_dstat.operations_read) + + (dstat[i].operations[DEVSTAT_WRITE] - dm->prev_dstat.operations_write)) : + 0); + rrdset_done(dm->st_svctm); + } + + // -------------------------------------------------------------------- + + dm->prev_dstat.bytes_read = dstat[i].bytes[DEVSTAT_READ]; + dm->prev_dstat.bytes_write = dstat[i].bytes[DEVSTAT_WRITE]; + dm->prev_dstat.operations_read = dstat[i].operations[DEVSTAT_READ]; + dm->prev_dstat.operations_write = dstat[i].operations[DEVSTAT_WRITE]; + dm->prev_dstat.duration_read_ms = cur_dstat.duration_read_ms; + dm->prev_dstat.duration_write_ms = cur_dstat.duration_write_ms; + dm->prev_dstat.busy_time_ms = cur_dstat.busy_time_ms; + } + } + } + + // -------------------------------------------------------------------- + + if (likely(do_system_io)) { + static RRDSET *st = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL; + + if (unlikely(!st)) { + st = rrdset_create_localhost("system", + "io", + NULL, + "disk", + NULL, + "Disk I/O", + "kilobytes/s", + 150, + update_every, + RRDSET_TYPE_AREA + ); + + rd_in = rrddim_add(st, "in", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_out = rrddim_add(st, "out", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + } else + rrdset_next(st); + + rrddim_set_by_pointer(st, rd_in, total_disk_kbytes_read); + rrddim_set_by_pointer(st, rd_out, total_disk_kbytes_write); + rrdset_done(st); + } + } + } + if (unlikely(common_error)) { + do_system_io = 0; + error("DISABLED: system.io chart"); + do_io = 0; + error("DISABLED: disk.* charts"); + do_ops = 0; + error("DISABLED: disk_ops.* charts"); + do_qops = 0; + error("DISABLED: disk_qops.* charts"); + do_util = 0; + error("DISABLED: disk_util.* charts"); + do_iotime = 0; + error("DISABLED: disk_iotime.* charts"); + do_await = 0; + error("DISABLED: disk_await.* charts"); + do_avagsz = 0; + error("DISABLED: disk_avgsz.* charts"); + do_svctm = 0; + error("DISABLED: disk_svctm.* charts"); + error("DISABLED: kern.devstat module"); + return 1; } + } else { + error("DISABLED: kern.devstat module"); + return 1; } return 0; diff --git a/src/freeipmi_plugin.c b/src/freeipmi_plugin.c new file mode 100644 index 000000000..4459de7ca --- /dev/null +++ b/src/freeipmi_plugin.c @@ -0,0 +1,1621 @@ +/* + * netdata freeipmi.plugin + * Copyright (C) 2017 Costa Tsaousis + * GPL v3+ + * + * Based on: + * ipmimonitoring-sensors.c,v 1.51 2016/11/02 23:46:24 chu11 Exp + * ipmimonitoring-sel.c,v 1.51 2016/11/02 23:46:24 chu11 Exp + * + * Copyright (C) 2007-2015 Lawrence Livermore National Security, LLC. + * Copyright (C) 2006-2007 The Regents of the University of California. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Albert Chu <chu11@llnl.gov> + * UCRL-CODE-222073 + */ + +#include "common.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include <sys/time.h> + +#ifdef HAVE_FREEIPMI + +#include <ipmi_monitoring.h> +#include <ipmi_monitoring_bitmasks.h> + +/* Communication Configuration - Initialize accordingly */ + +/* Hostname, NULL for In-band communication, non-null for a hostname */ +char *hostname = NULL; + +/* In-band Communication Configuration */ +int driver_type = -1; // IPMI_MONITORING_DRIVER_TYPE_KCS; /* or -1 for default */ +int disable_auto_probe = 0; /* probe for in-band device */ +unsigned int driver_address = 0; /* not used if probing */ +unsigned int register_spacing = 0; /* not used if probing */ +char *driver_device = NULL; /* not used if probing */ + +/* Out-of-band Communication Configuration */ +int protocol_version = -1; //IPMI_MONITORING_PROTOCOL_VERSION_1_5; /* or -1 for default */ +char *username = "foousername"; +char *password = "foopassword"; +unsigned char *k_g = NULL; +unsigned int k_g_len = 0; +int privilege_level = -1; // IPMI_MONITORING_PRIVILEGE_LEVEL_USER; /* or -1 for default */ +int authentication_type = -1; // IPMI_MONITORING_AUTHENTICATION_TYPE_MD5; /* or -1 for default */ +int cipher_suite_id = 0; /* or -1 for default */ +int session_timeout = 0; /* 0 for default */ +int retransmission_timeout = 0; /* 0 for default */ + +/* Workarounds - specify workaround flags if necessary */ +unsigned int workaround_flags = 0; + +/* Initialize w/ record id numbers to only monitor specific record ids */ +unsigned int record_ids[] = {0}; +unsigned int record_ids_length = 0; + +/* Initialize w/ sensor types to only monitor specific sensor types + * see ipmi_monitoring.h sensor types list. + */ +unsigned int sensor_types[] = {0}; +unsigned int sensor_types_length = 0; + +/* Set to an appropriate alternate if desired */ +char *sdr_cache_directory = "/tmp"; +char *sensor_config_file = NULL; + +/* Set to 1 or 0 to enable these sensor reading flags + * - See ipmi_monitoring.h for descriptions of these flags. + */ +int reread_sdr_cache = 0; +int ignore_non_interpretable_sensors = 1; +int bridge_sensors = 0; +int interpret_oem_data = 0; +int shared_sensors = 0; +int discrete_reading = 0; +int ignore_scanning_disabled = 0; +int assume_bmc_owner = 0; +int entity_sensor_names = 0; + +/* Initialization flags + * + * Most commonly bitwise OR IPMI_MONITORING_FLAGS_DEBUG and/or + * IPMI_MONITORING_FLAGS_DEBUG_IPMI_PACKETS for extra debugging + * information. + */ +unsigned int ipmimonitoring_init_flags = 0; + +int errnum; + +// ---------------------------------------------------------------------------- +// SEL only variables + +/* Initialize w/ date range to only monitoring specific date range */ +char *date_begin = NULL; /* use MM/DD/YYYY format */ +char *date_end = NULL; /* use MM/DD/YYYY format */ + +int assume_system_event_record = 0; + +char *sel_config_file = NULL; + + +// ---------------------------------------------------------------------------- +// functions common to sensors and SEL + +static void +_init_ipmi_config (struct ipmi_monitoring_ipmi_config *ipmi_config) +{ + assert (ipmi_config); + + ipmi_config->driver_type = driver_type; + ipmi_config->disable_auto_probe = disable_auto_probe; + ipmi_config->driver_address = driver_address; + ipmi_config->register_spacing = register_spacing; + ipmi_config->driver_device = driver_device; + + ipmi_config->protocol_version = protocol_version; + ipmi_config->username = username; + ipmi_config->password = password; + ipmi_config->k_g = k_g; + ipmi_config->k_g_len = k_g_len; + ipmi_config->privilege_level = privilege_level; + ipmi_config->authentication_type = authentication_type; + ipmi_config->cipher_suite_id = cipher_suite_id; + ipmi_config->session_timeout_len = session_timeout; + ipmi_config->retransmission_timeout_len = retransmission_timeout; + + ipmi_config->workaround_flags = workaround_flags; +} + +#ifdef NETDATA_COMMENTED +static const char * +_get_sensor_type_string (int sensor_type) +{ + switch (sensor_type) + { + case IPMI_MONITORING_SENSOR_TYPE_RESERVED: + return ("Reserved"); + case IPMI_MONITORING_SENSOR_TYPE_TEMPERATURE: + return ("Temperature"); + case IPMI_MONITORING_SENSOR_TYPE_VOLTAGE: + return ("Voltage"); + case IPMI_MONITORING_SENSOR_TYPE_CURRENT: + return ("Current"); + case IPMI_MONITORING_SENSOR_TYPE_FAN: + return ("Fan"); + case IPMI_MONITORING_SENSOR_TYPE_PHYSICAL_SECURITY: + return ("Physical Security"); + case IPMI_MONITORING_SENSOR_TYPE_PLATFORM_SECURITY_VIOLATION_ATTEMPT: + return ("Platform Security Violation Attempt"); + case IPMI_MONITORING_SENSOR_TYPE_PROCESSOR: + return ("Processor"); + case IPMI_MONITORING_SENSOR_TYPE_POWER_SUPPLY: + return ("Power Supply"); + case IPMI_MONITORING_SENSOR_TYPE_POWER_UNIT: + return ("Power Unit"); + case IPMI_MONITORING_SENSOR_TYPE_COOLING_DEVICE: + return ("Cooling Device"); + case IPMI_MONITORING_SENSOR_TYPE_OTHER_UNITS_BASED_SENSOR: + return ("Other Units Based Sensor"); + case IPMI_MONITORING_SENSOR_TYPE_MEMORY: + return ("Memory"); + case IPMI_MONITORING_SENSOR_TYPE_DRIVE_SLOT: + return ("Drive Slot"); + case IPMI_MONITORING_SENSOR_TYPE_POST_MEMORY_RESIZE: + return ("POST Memory Resize"); + case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_FIRMWARE_PROGRESS: + return ("System Firmware Progress"); + case IPMI_MONITORING_SENSOR_TYPE_EVENT_LOGGING_DISABLED: + return ("Event Logging Disabled"); + case IPMI_MONITORING_SENSOR_TYPE_WATCHDOG1: + return ("Watchdog 1"); + case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_EVENT: + return ("System Event"); + case IPMI_MONITORING_SENSOR_TYPE_CRITICAL_INTERRUPT: + return ("Critical Interrupt"); + case IPMI_MONITORING_SENSOR_TYPE_BUTTON_SWITCH: + return ("Button/Switch"); + case IPMI_MONITORING_SENSOR_TYPE_MODULE_BOARD: + return ("Module/Board"); + case IPMI_MONITORING_SENSOR_TYPE_MICROCONTROLLER_COPROCESSOR: + return ("Microcontroller/Coprocessor"); + case IPMI_MONITORING_SENSOR_TYPE_ADD_IN_CARD: + return ("Add In Card"); + case IPMI_MONITORING_SENSOR_TYPE_CHASSIS: + return ("Chassis"); + case IPMI_MONITORING_SENSOR_TYPE_CHIP_SET: + return ("Chip Set"); + case IPMI_MONITORING_SENSOR_TYPE_OTHER_FRU: + return ("Other Fru"); + case IPMI_MONITORING_SENSOR_TYPE_CABLE_INTERCONNECT: + return ("Cable/Interconnect"); + case IPMI_MONITORING_SENSOR_TYPE_TERMINATOR: + return ("Terminator"); + case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_BOOT_INITIATED: + return ("System Boot Initiated"); + case IPMI_MONITORING_SENSOR_TYPE_BOOT_ERROR: + return ("Boot Error"); + case IPMI_MONITORING_SENSOR_TYPE_OS_BOOT: + return ("OS Boot"); + case IPMI_MONITORING_SENSOR_TYPE_OS_CRITICAL_STOP: + return ("OS Critical Stop"); + case IPMI_MONITORING_SENSOR_TYPE_SLOT_CONNECTOR: + return ("Slot/Connector"); + case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_ACPI_POWER_STATE: + return ("System ACPI Power State"); + case IPMI_MONITORING_SENSOR_TYPE_WATCHDOG2: + return ("Watchdog 2"); + case IPMI_MONITORING_SENSOR_TYPE_PLATFORM_ALERT: + return ("Platform Alert"); + case IPMI_MONITORING_SENSOR_TYPE_ENTITY_PRESENCE: + return ("Entity Presence"); + case IPMI_MONITORING_SENSOR_TYPE_MONITOR_ASIC_IC: + return ("Monitor ASIC/IC"); + case IPMI_MONITORING_SENSOR_TYPE_LAN: + return ("LAN"); + case IPMI_MONITORING_SENSOR_TYPE_MANAGEMENT_SUBSYSTEM_HEALTH: + return ("Management Subsystem Health"); + case IPMI_MONITORING_SENSOR_TYPE_BATTERY: + return ("Battery"); + case IPMI_MONITORING_SENSOR_TYPE_SESSION_AUDIT: + return ("Session Audit"); + case IPMI_MONITORING_SENSOR_TYPE_VERSION_CHANGE: + return ("Version Change"); + case IPMI_MONITORING_SENSOR_TYPE_FRU_STATE: + return ("FRU State"); + } + + return ("Unrecognized"); +} +#endif // NETDATA_COMMENTED + + +// ---------------------------------------------------------------------------- +// BEGIN NETDATA CODE + +static int debug = 0; + +static int netdata_update_every = 5; +static int netdata_priority = 90000; +static int netdata_do_sel = 1; + +static size_t netdata_sensors_updated = 0; +static size_t netdata_sensors_collected = 0; +static size_t netdata_sel_events = 0; +static size_t netdata_sensors_states_nominal = 0; +static size_t netdata_sensors_states_warning = 0; +static size_t netdata_sensors_states_critical = 0; + +struct sensor { + int record_id; + int sensor_number; + int sensor_type; + int sensor_state; + int sensor_units; + char *sensor_name; + + int sensor_reading_type; + union { + uint8_t bool_value; + uint32_t uint32_value; + double double_value; + } sensor_reading; + + int sent; + int ignore; + int exposed; + int updated; + struct sensor *next; +} *sensors_root = NULL; + +static void netdata_mark_as_not_updated() { + struct sensor *sn; + for(sn = sensors_root; sn ;sn = sn->next) + sn->updated = sn->sent = 0; + + netdata_sensors_updated = 0; + netdata_sensors_collected = 0; + netdata_sel_events = 0; + + netdata_sensors_states_nominal = 0; + netdata_sensors_states_warning = 0; + netdata_sensors_states_critical = 0; +} + +static void send_chart_to_netdata_for_units(int units) { + struct sensor *sn; + + switch(units) { + case IPMI_MONITORING_SENSOR_UNITS_CELSIUS: + printf("CHART ipmi.temperatures_c '' 'System Celcius Temperatures read by IPMI' 'Celcius' 'temperatures' 'ipmi.temperatures_c' 'line' %d %d\n" + , netdata_priority + 10 + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_FAHRENHEIT: + printf("CHART ipmi.temperatures_f '' 'System Fahrenheit Temperatures read by IPMI' 'Fahrenheit' 'temperatures' 'ipmi.temperatures_f' 'line' %d %d\n" + , netdata_priority + 11 + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_VOLTS: + printf("CHART ipmi.volts '' 'System Voltages read by IPMI' 'Volts' 'voltages' 'ipmi.voltages' 'line' %d %d\n" + , netdata_priority + 12 + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_AMPS: + printf("CHART ipmi.amps '' 'System Current read by IPMI' 'Amps' 'current' 'ipmi.amps' 'line' %d %d\n" + , netdata_priority + 13 + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_RPM: + printf("CHART ipmi.rpm '' 'System Fans read by IPMI' 'RPM' 'fans' 'ipmi.rpm' 'line' %d %d\n" + , netdata_priority + 14 + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_WATTS: + printf("CHART ipmi.watts '' 'System Power read by IPMI' 'Watts' 'power' 'ipmi.watts' 'line' %d %d\n" + , netdata_priority + 5 + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_PERCENT: + printf("CHART ipmi.percent '' 'System Metrics read by IPMI' '%%' 'other' 'ipmi.percent' 'line' %d %d\n" + , netdata_priority + 15 + , netdata_update_every + ); + break; + + default: + for(sn = sensors_root; sn; sn = sn->next) + if(sn->sensor_units == units) + sn->ignore = 1; + return; + } + + for(sn = sensors_root; sn; sn = sn->next) { + if(sn->sensor_units == units && sn->updated && !sn->ignore) { + sn->exposed = 1; + + switch(sn->sensor_reading_type) { + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: + printf("DIMENSION i%d_n%d_r%d '%s i%d' absolute 1 1\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , sn->sensor_name + , sn->sensor_number + ); + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: + printf("DIMENSION i%d_n%d_r%d '%s i%d' absolute 1 1000\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , sn->sensor_name + , sn->sensor_number + ); + break; + + default: + sn->ignore = 1; + break; + } + } + } +} + +static void send_metrics_to_netdata_for_units(int units) { + struct sensor *sn; + + switch(units) { + case IPMI_MONITORING_SENSOR_UNITS_CELSIUS: + printf("BEGIN ipmi.temperatures_c\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_FAHRENHEIT: + printf("BEGIN ipmi.temperatures_f\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_VOLTS: + printf("BEGIN ipmi.volts\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_AMPS: + printf("BEGIN ipmi.amps\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_RPM: + printf("BEGIN ipmi.rpm\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_WATTS: + printf("BEGIN ipmi.watts\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_PERCENT: + printf("BEGIN ipmi.percent\n"); + break; + + default: + for(sn = sensors_root; sn; sn = sn->next) + if(sn->sensor_units == units) + sn->ignore = 1; + return; + } + + for(sn = sensors_root; sn; sn = sn->next) { + if(sn->sensor_units == units && sn->updated && !sn->sent && !sn->ignore) { + netdata_sensors_updated++; + + sn->sent = 1; + + switch(sn->sensor_reading_type) { + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: + printf("SET i%d_n%d_r%d = %u\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , sn->sensor_reading.bool_value + ); + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: + printf("SET i%d_n%d_r%d = %u\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , sn->sensor_reading.uint32_value + ); + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: + printf("SET i%d_n%d_r%d = %lld\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , (long long int)(sn->sensor_reading.double_value * 1000) + ); + break; + + default: + sn->ignore = 1; + break; + } + } + } + + printf("END\n"); +} + +static void send_metrics_to_netdata() { + static int sel_chart_generated = 0, sensors_states_chart_generated = 0; + struct sensor *sn; + + if(netdata_do_sel && !sel_chart_generated) { + sel_chart_generated = 1; + printf("CHART ipmi.events '' 'IPMI Events' 'events' 'events' ipmi.sel area %d %d\n" + , netdata_priority + 2 + , netdata_update_every + ); + printf("DIMENSION events '' absolute 1 1\n"); + } + + if(!sensors_states_chart_generated) { + sensors_states_chart_generated = 1; + printf("CHART ipmi.sensors_states '' 'IPMI Sensors State' 'sensors' 'states' ipmi.sensors_states line %d %d\n" + , netdata_priority + 1 + , netdata_update_every + ); + printf("DIMENSION nominal '' absolute 1 1\n"); + printf("DIMENSION critical '' absolute 1 1\n"); + printf("DIMENSION warning '' absolute 1 1\n"); + } + + // generate the CHART/DIMENSION lines, if we have to + for(sn = sensors_root; sn; sn = sn->next) + if(sn->updated && !sn->exposed && !sn->ignore) + send_chart_to_netdata_for_units(sn->sensor_units); + + if(netdata_do_sel) { + printf( + "BEGIN ipmi.events\n" + "SET events = %zu\n" + "END\n" + , netdata_sel_events + ); + } + + printf( + "BEGIN ipmi.sensors_states\n" + "SET nominal = %zu\n" + "SET warning = %zu\n" + "SET critical = %zu\n" + "END\n" + , netdata_sensors_states_nominal + , netdata_sensors_states_warning + , netdata_sensors_states_critical + ); + + // send metrics to netdata + for(sn = sensors_root; sn; sn = sn->next) + if(sn->updated && sn->exposed && !sn->sent && !sn->ignore) + send_metrics_to_netdata_for_units(sn->sensor_units); + +} + +static void netdata_get_sensor( + int record_id + , int sensor_number + , int sensor_type + , int sensor_state + , int sensor_units + , int sensor_reading_type + , char *sensor_name + , void *sensor_reading +) { + // find the sensor record + struct sensor *sn; + for(sn = sensors_root; sn ;sn = sn->next) + if( sn->record_id == record_id && + sn->sensor_number == sensor_number && + sn->sensor_reading_type == sensor_reading_type && + sn->sensor_units == sensor_units && + !strcmp(sn->sensor_name, sensor_name) + ) + break; + + if(!sn) { + // not found, create it + + sn = calloc(1, sizeof(struct sensor)); + if(!sn) { + fatal("cannot allocate %zu bytes of memory.", sizeof(struct sensor)); + } + + sn->record_id = record_id; + sn->sensor_number = sensor_number; + sn->sensor_type = sensor_type; + sn->sensor_state = sensor_state; + sn->sensor_units = sensor_units; + sn->sensor_reading_type = sensor_reading_type; + sn->sensor_name = strdup(sensor_name); + if(!sn->sensor_name) { + fatal("cannot allocate %zu bytes of memory.", strlen(sensor_name)); + } + + sn->next = sensors_root; + sensors_root = sn; + } + + switch(sensor_reading_type) { + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: + sn->sensor_reading.bool_value = *((uint8_t *)sensor_reading); + sn->updated = 1; + netdata_sensors_collected++; + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: + sn->sensor_reading.uint32_value = *((uint32_t *)sensor_reading); + sn->updated = 1; + netdata_sensors_collected++; + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: + sn->sensor_reading.double_value = *((double *)sensor_reading); + sn->updated = 1; + netdata_sensors_collected++; + break; + + default: + sn->ignore = 1; + break; + } + + switch(sensor_state) { + case IPMI_MONITORING_STATE_NOMINAL: + netdata_sensors_states_nominal++; + break; + + case IPMI_MONITORING_STATE_WARNING: + netdata_sensors_states_warning++; + break; + + case IPMI_MONITORING_STATE_CRITICAL: + netdata_sensors_states_critical++; + break; + + default: + break; + } +} + +static void netdata_get_sel( + int record_id + , int record_type_class + , int sel_state +) { + (void)record_id; + (void)record_type_class; + (void)sel_state; + + netdata_sel_events++; +} + + +void netdata_cleanup_and_exit(int ret) { + exit(ret); +} + +// END NETDATA CODE +// ---------------------------------------------------------------------------- + + +static int +_ipmimonitoring_sensors (struct ipmi_monitoring_ipmi_config *ipmi_config) +{ + ipmi_monitoring_ctx_t ctx = NULL; + unsigned int sensor_reading_flags = 0; + int i; + int sensor_count; + int rv = -1; + + if (!(ctx = ipmi_monitoring_ctx_create ())) { + error("ipmi_monitoring_ctx_create()"); + goto cleanup; + } + + if (sdr_cache_directory) + { + if (ipmi_monitoring_ctx_sdr_cache_directory (ctx, + sdr_cache_directory) < 0) + { + error("ipmi_monitoring_ctx_sdr_cache_directory(): %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + + /* Must call otherwise only default interpretations ever used */ + if (sensor_config_file) + { + if (ipmi_monitoring_ctx_sensor_config_file (ctx, + sensor_config_file) < 0) + { + error( "ipmi_monitoring_ctx_sensor_config_file(): %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else + { + if (ipmi_monitoring_ctx_sensor_config_file (ctx, NULL) < 0) + { + error( "ipmi_monitoring_ctx_sensor_config_file(): %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + + if (reread_sdr_cache) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_REREAD_SDR_CACHE; + + if (ignore_non_interpretable_sensors) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_NON_INTERPRETABLE_SENSORS; + + if (bridge_sensors) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_BRIDGE_SENSORS; + + if (interpret_oem_data) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_INTERPRET_OEM_DATA; + + if (shared_sensors) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_SHARED_SENSORS; + + if (discrete_reading) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_DISCRETE_READING; + + if (ignore_scanning_disabled) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_SCANNING_DISABLED; + + if (assume_bmc_owner) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_ASSUME_BMC_OWNER; + +#ifdef IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES + if (entity_sensor_names) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES; +#endif // IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES + + if (!record_ids_length && !sensor_types_length) + { + if ((sensor_count = ipmi_monitoring_sensor_readings_by_record_id (ctx, + hostname, + ipmi_config, + sensor_reading_flags, + NULL, + 0, + NULL, + NULL)) < 0) + { + error( "ipmi_monitoring_sensor_readings_by_record_id(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else if (record_ids_length) + { + if ((sensor_count = ipmi_monitoring_sensor_readings_by_record_id (ctx, + hostname, + ipmi_config, + sensor_reading_flags, + record_ids, + record_ids_length, + NULL, + NULL)) < 0) + { + error( "ipmi_monitoring_sensor_readings_by_record_id(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else + { + if ((sensor_count = ipmi_monitoring_sensor_readings_by_sensor_type (ctx, + hostname, + ipmi_config, + sensor_reading_flags, + sensor_types, + sensor_types_length, + NULL, + NULL)) < 0) + { + error( "ipmi_monitoring_sensor_readings_by_sensor_type(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + +#ifdef NETDATA_COMMENTED + printf ("%s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", + "Record ID", + "Sensor Name", + "Sensor Number", + "Sensor Type", + "Sensor State", + "Sensor Reading", + "Sensor Units", + "Sensor Event/Reading Type Code", + "Sensor Event Bitmask", + "Sensor Event String"); +#endif // NETDATA_COMMENTED + + for (i = 0; i < sensor_count; i++, ipmi_monitoring_sensor_iterator_next (ctx)) + { + int record_id, sensor_number, sensor_type, sensor_state, sensor_units, + sensor_reading_type; + +#ifdef NETDATA_COMMENTED + int sensor_bitmask_type, sensor_bitmask, event_reading_type_code; + char **sensor_bitmask_strings = NULL; + const char *sensor_type_str; + const char *sensor_state_str; +#endif // NETDATA_COMMENTED + + char *sensor_name = NULL; + void *sensor_reading; + + if ((record_id = ipmi_monitoring_sensor_read_record_id (ctx)) < 0) + { + error( "ipmi_monitoring_sensor_read_record_id(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_number = ipmi_monitoring_sensor_read_sensor_number (ctx)) < 0) + { + error( "ipmi_monitoring_sensor_read_sensor_number(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_type = ipmi_monitoring_sensor_read_sensor_type (ctx)) < 0) + { + error( "ipmi_monitoring_sensor_read_sensor_type(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (!(sensor_name = ipmi_monitoring_sensor_read_sensor_name (ctx))) + { + error( "ipmi_monitoring_sensor_read_sensor_name(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_state = ipmi_monitoring_sensor_read_sensor_state (ctx)) < 0) + { + error( "ipmi_monitoring_sensor_read_sensor_state(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_units = ipmi_monitoring_sensor_read_sensor_units (ctx)) < 0) + { + error( "ipmi_monitoring_sensor_read_sensor_units(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + +#ifdef NETDATA_COMMENTED + if ((sensor_bitmask_type = ipmi_monitoring_sensor_read_sensor_bitmask_type (ctx)) < 0) + { + error( "ipmi_monitoring_sensor_read_sensor_bitmask_type(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + if ((sensor_bitmask = ipmi_monitoring_sensor_read_sensor_bitmask (ctx)) < 0) + { + error( + "ipmi_monitoring_sensor_read_sensor_bitmask(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (!(sensor_bitmask_strings = ipmi_monitoring_sensor_read_sensor_bitmask_strings (ctx))) + { + error( "ipmi_monitoring_sensor_read_sensor_bitmask_strings(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } +#endif // NETDATA_COMMENTED + + if ((sensor_reading_type = ipmi_monitoring_sensor_read_sensor_reading_type (ctx)) < 0) + { + error( "ipmi_monitoring_sensor_read_sensor_reading_type(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + sensor_reading = ipmi_monitoring_sensor_read_sensor_reading (ctx); + +#ifdef NETDATA_COMMENTED + if ((event_reading_type_code = ipmi_monitoring_sensor_read_event_reading_type_code (ctx)) < 0) + { + error( "ipmi_monitoring_sensor_read_event_reading_type_code(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } +#endif // NETDATA_COMMENTED + + netdata_get_sensor( + record_id + , sensor_number + , sensor_type + , sensor_state + , sensor_units + , sensor_reading_type + , sensor_name + , sensor_reading + ); + +#ifdef NETDATA_COMMENTED + if (!strlen (sensor_name)) + sensor_name = "N/A"; + + sensor_type_str = _get_sensor_type_string (sensor_type); + + printf ("%d, %s, %d, %s", + record_id, + sensor_name, + sensor_number, + sensor_type_str); + + if (sensor_state == IPMI_MONITORING_STATE_NOMINAL) + sensor_state_str = "Nominal"; + else if (sensor_state == IPMI_MONITORING_STATE_WARNING) + sensor_state_str = "Warning"; + else if (sensor_state == IPMI_MONITORING_STATE_CRITICAL) + sensor_state_str = "Critical"; + else + sensor_state_str = "N/A"; + + printf (", %s", sensor_state_str); + + if (sensor_reading) + { + const char *sensor_units_str; + + if (sensor_reading_type == IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL) + printf (", %s", + (*((uint8_t *)sensor_reading) ? "true" : "false")); + else if (sensor_reading_type == IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32) + printf (", %u", + *((uint32_t *)sensor_reading)); + else if (sensor_reading_type == IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE) + printf (", %.2f", + *((double *)sensor_reading)); + else + printf (", N/A"); + + if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_CELSIUS) + sensor_units_str = "C"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_FAHRENHEIT) + sensor_units_str = "F"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_VOLTS) + sensor_units_str = "V"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_AMPS) + sensor_units_str = "A"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_RPM) + sensor_units_str = "RPM"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_WATTS) + sensor_units_str = "W"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_PERCENT) + sensor_units_str = "%"; + else + sensor_units_str = "N/A"; + + printf (", %s", sensor_units_str); + } + else + printf (", N/A, N/A"); + + printf (", %Xh", event_reading_type_code); + + /* It is possible you may want to monitor specific event + * conditions that may occur. If that is the case, you may want + * to check out what specific bitmask type and bitmask events + * occurred. See ipmi_monitoring_bitmasks.h for a list of + * bitmasks and types. + */ + + if (sensor_bitmask_type != IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN) + printf (", %Xh", sensor_bitmask); + else + printf (", N/A"); + + if (sensor_bitmask_type != IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN) + { + unsigned int i = 0; + + printf (","); + + while (sensor_bitmask_strings[i]) + { + printf (" "); + + printf ("'%s'", + sensor_bitmask_strings[i]); + + i++; + } + } + else + printf (", N/A"); + + printf ("\n"); +#endif // NETDATA_COMMENTED + } + + rv = 0; + cleanup: + if (ctx) + ipmi_monitoring_ctx_destroy (ctx); + return (rv); +} + + +static int +_ipmimonitoring_sel (struct ipmi_monitoring_ipmi_config *ipmi_config) +{ + ipmi_monitoring_ctx_t ctx = NULL; + unsigned int sel_flags = 0; + int i; + int sel_count; + int rv = -1; + + if (!(ctx = ipmi_monitoring_ctx_create ())) + { + error("ipmi_monitoring_ctx_create()"); + goto cleanup; + } + + if (sdr_cache_directory) + { + if (ipmi_monitoring_ctx_sdr_cache_directory (ctx, + sdr_cache_directory) < 0) + { + error( "ipmi_monitoring_ctx_sdr_cache_directory(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + + /* Must call otherwise only default interpretations ever used */ + if (sel_config_file) + { + if (ipmi_monitoring_ctx_sel_config_file (ctx, + sel_config_file) < 0) + { + error( "ipmi_monitoring_ctx_sel_config_file(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else + { + if (ipmi_monitoring_ctx_sel_config_file (ctx, NULL) < 0) + { + error( "ipmi_monitoring_ctx_sel_config_file(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + + if (reread_sdr_cache) + sel_flags |= IPMI_MONITORING_SEL_FLAGS_REREAD_SDR_CACHE; + + if (interpret_oem_data) + sel_flags |= IPMI_MONITORING_SEL_FLAGS_INTERPRET_OEM_DATA; + + if (assume_system_event_record) + sel_flags |= IPMI_MONITORING_SEL_FLAGS_ASSUME_SYSTEM_EVENT_RECORD; + +#ifdef IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES + if (entity_sensor_names) + sel_flags |= IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES; +#endif // IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES + + if (record_ids_length) + { + if ((sel_count = ipmi_monitoring_sel_by_record_id (ctx, + hostname, + ipmi_config, + sel_flags, + record_ids, + record_ids_length, + NULL, + NULL)) < 0) + { + error( "ipmi_monitoring_sel_by_record_id(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else if (sensor_types_length) + { + if ((sel_count = ipmi_monitoring_sel_by_sensor_type (ctx, + hostname, + ipmi_config, + sel_flags, + sensor_types, + sensor_types_length, + NULL, + NULL)) < 0) + { + error( "ipmi_monitoring_sel_by_sensor_type(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else if (date_begin + || date_end) + { + if ((sel_count = ipmi_monitoring_sel_by_date_range (ctx, + hostname, + ipmi_config, + sel_flags, + date_begin, + date_end, + NULL, + NULL)) < 0) + { + error( "ipmi_monitoring_sel_by_sensor_type(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else + { + if ((sel_count = ipmi_monitoring_sel_by_record_id (ctx, + hostname, + ipmi_config, + sel_flags, + NULL, + 0, + NULL, + NULL)) < 0) + { + error( "ipmi_monitoring_sel_by_record_id(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + +#ifdef NETDATA_COMMENTED + printf ("%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", + "Record ID", + "Record Type", + "SEL State", + "Timestamp", + "Sensor Name", + "Sensor Type", + "Event Direction", + "Event Type Code", + "Event Data", + "Event Offset", + "Event Offset String"); +#endif // NETDATA_COMMENTED + + for (i = 0; i < sel_count; i++, ipmi_monitoring_sel_iterator_next (ctx)) + { + int record_id, record_type, sel_state, record_type_class; +#ifdef NETDATA_COMMENTED + int sensor_type, sensor_number, event_direction, + event_offset_type, event_offset, event_type_code, manufacturer_id; + unsigned int timestamp, event_data1, event_data2, event_data3; + char *event_offset_string = NULL; + const char *sensor_type_str; + const char *event_direction_str; + const char *sel_state_str; + char *sensor_name = NULL; + unsigned char oem_data[64]; + int oem_data_len; + unsigned int j; +#endif // NETDATA_COMMENTED + + if ((record_id = ipmi_monitoring_sel_read_record_id (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_record_id(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((record_type = ipmi_monitoring_sel_read_record_type (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_record_type(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((record_type_class = ipmi_monitoring_sel_read_record_type_class (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_record_type_class(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sel_state = ipmi_monitoring_sel_read_sel_state (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_sel_state(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + netdata_get_sel( + record_id + , record_type_class + , sel_state + ); + +#ifdef NETDATA_COMMENTED + if (sel_state == IPMI_MONITORING_STATE_NOMINAL) + sel_state_str = "Nominal"; + else if (sel_state == IPMI_MONITORING_STATE_WARNING) + sel_state_str = "Warning"; + else if (sel_state == IPMI_MONITORING_STATE_CRITICAL) + sel_state_str = "Critical"; + else + sel_state_str = "N/A"; + + printf ("%d, %d, %s", + record_id, + record_type, + sel_state_str); + + if (record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_SYSTEM_EVENT_RECORD + || record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_TIMESTAMPED_OEM_RECORD) + { + + if (ipmi_monitoring_sel_read_timestamp (ctx, ×tamp) < 0) + { + error( "ipmi_monitoring_sel_read_timestamp(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + /* XXX: This should be converted to a nice date output using + * your favorite timestamp -> string conversion functions. + */ + printf (", %u", timestamp); + } + else + printf (", N/A"); + + if (record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_SYSTEM_EVENT_RECORD) + { + /* If you are integrating ipmimonitoring SEL into a monitoring application, + * you may wish to count the number of times a specific error occurred + * and report that to the monitoring application. + * + * In this particular case, you'll probably want to check out + * what sensor type each SEL event is reporting, the + * event offset type, and the specific event offset that occurred. + * + * See ipmi_monitoring_offsets.h for a list of event offsets + * and types. + */ + + if (!(sensor_name = ipmi_monitoring_sel_read_sensor_name (ctx))) + { + error( "ipmi_monitoring_sel_read_sensor_name(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_type = ipmi_monitoring_sel_read_sensor_type (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_sensor_type(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_number = ipmi_monitoring_sel_read_sensor_number (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_sensor_number(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((event_direction = ipmi_monitoring_sel_read_event_direction (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_event_direction(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((event_type_code = ipmi_monitoring_sel_read_event_type_code (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_event_type_code(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (ipmi_monitoring_sel_read_event_data (ctx, + &event_data1, + &event_data2, + &event_data3) < 0) + { + error( "ipmi_monitoring_sel_read_event_data(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((event_offset_type = ipmi_monitoring_sel_read_event_offset_type (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_event_offset_type(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((event_offset = ipmi_monitoring_sel_read_event_offset (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_event_offset(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (!(event_offset_string = ipmi_monitoring_sel_read_event_offset_string (ctx))) + { + error( "ipmi_monitoring_sel_read_event_offset_string(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (!strlen (sensor_name)) + sensor_name = "N/A"; + + sensor_type_str = _get_sensor_type_string (sensor_type); + + if (event_direction == IPMI_MONITORING_SEL_EVENT_DIRECTION_ASSERTION) + event_direction_str = "Assertion"; + else + event_direction_str = "Deassertion"; + + printf (", %s, %s, %d, %s, %Xh, %Xh-%Xh-%Xh", + sensor_name, + sensor_type_str, + sensor_number, + event_direction_str, + event_type_code, + event_data1, + event_data2, + event_data3); + + if (event_offset_type != IPMI_MONITORING_EVENT_OFFSET_TYPE_UNKNOWN) + printf (", %Xh", event_offset); + else + printf (", N/A"); + + if (event_offset_type != IPMI_MONITORING_EVENT_OFFSET_TYPE_UNKNOWN) + printf (", %s", event_offset_string); + else + printf (", N/A"); + } + else if (record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_TIMESTAMPED_OEM_RECORD + || record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_NON_TIMESTAMPED_OEM_RECORD) + { + if (record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_TIMESTAMPED_OEM_RECORD) + { + if ((manufacturer_id = ipmi_monitoring_sel_read_manufacturer_id (ctx)) < 0) + { + error( "ipmi_monitoring_sel_read_manufacturer_id(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + printf (", Manufacturer ID = %Xh", manufacturer_id); + } + + if ((oem_data_len = ipmi_monitoring_sel_read_oem_data (ctx, oem_data, 1024)) < 0) + { + error( "ipmi_monitoring_sel_read_oem_data(): %s", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + printf (", OEM Data = "); + + for (j = 0; j < oem_data_len; j++) + printf ("%02Xh ", oem_data[j]); + } + else + printf (", N/A, N/A, N/A, N/A, N/A, N/A, N/A"); + + printf ("\n"); +#endif // NETDATA_COMMENTED + } + + rv = 0; + cleanup: + if (ctx) + ipmi_monitoring_ctx_destroy (ctx); + return (rv); +} + +// ---------------------------------------------------------------------------- +// MAIN PROGRAM FOR NETDATA PLUGIN + +int ipmi_collect_data(struct ipmi_monitoring_ipmi_config *ipmi_config) { + errno = 0; + + if (_ipmimonitoring_sensors(ipmi_config) < 0) return -1; + + if(netdata_do_sel) { + if(_ipmimonitoring_sel(ipmi_config) < 0) return -2; + } + + return 0; +} + +int ipmi_detect_speed_secs(struct ipmi_monitoring_ipmi_config *ipmi_config) { + int i, checks = 10; + unsigned long long total = 0; + + for(i = 0 ; i < checks ; i++) { + if(debug) fprintf(stderr, "freeipmi.plugin: checking data collection speed iteration %d of %d\n", i+1, checks); + + // measure the time a data collection needs + unsigned long long start = now_realtime_usec(); + if(ipmi_collect_data(ipmi_config) < 0) + fatal("freeipmi.plugin: data collection failed."); + + unsigned long long end = now_realtime_usec(); + + if(debug) fprintf(stderr, "freeipmi.plugin: data collection speed was %llu usec\n", end - start); + + // add it to our total + total += end - start; + + // wait the same time + // to avoid flooding the IPMI processor with requests + sleep_usec(end - start); + } + + // so, we assume it needed 2x the time + // we find the average in microseconds + // and we round-up to the closest second + + return (( total * 2 / checks / 1000000 ) + 1); +} + +int main (int argc, char **argv) { + + // ------------------------------------------------------------------------ + // initialization of netdata plugin + + program_name = "freeipmi.plugin"; + + // disable syslog + error_log_syslog = 0; + + // set errors flood protection to 100 logs per hour + error_log_errors_per_period = 100; + error_log_throttle_period = 3600; + + + // ------------------------------------------------------------------------ + // parse command line parameters + + int i, freq = 0; + for(i = 1; i < argc ; i++) { + if(!freq) { + int n = atoi(argv[i]); + if(n > 0) { + freq = n; + continue; + } + } + + if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) { + printf("freeipmi.plugin %s\n", VERSION); + exit(0); + } + else if(strcmp("debug", argv[i]) == 0) { + debug = 1; + continue; + } + else if(strcmp("sel", argv[i]) == 0) { + netdata_do_sel = 1; + continue; + } + else if(strcmp("no-sel", argv[i]) == 0) { + netdata_do_sel = 0; + continue; + } + else if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) { + fprintf(stderr, + "\n" + " netdata freeipmi.plugin %s\n" + " Copyright (C) 2016-2017 Costa Tsaousis <costa@tsaousis.gr>\n" + " Released under GNU General Public License v3 or later.\n" + " All rights reserved.\n" + "\n" + " This program is a data collector plugin for netdata.\n" + "\n" + " Available command line options:\n" + "\n" + " SECONDS data collection frequency\n" + " minimum: %d\n" + "\n" + " debug enable verbose output\n" + " default: disabled\n" + "\n" + " sel\n" + " no-sel enable/disable SEL collection\n" + " default: %s\n" + "\n" + " hostname HOST\n" + " username USER\n" + " password PASS connect to remote IPMI host\n" + " default: local IPMI processor\n" + "\n" + " sdr-cache-dir PATH directory for SDR cache files\n" + " default: %s\n" + "\n" + " sensor-config-file FILE filename to read sensor configuration\n" + " default: %s\n" + "\n" + " -v\n" + " -V\n" + " version print version and exit\n" + "\n" + " Linux kernel module for IPMI is CPU hungry.\n" + " On Linux run this to lower kipmiN CPU utilization:\n" + " # echo 10 > /sys/module/ipmi_si/parameters/kipmid_max_busy_us\n" + "\n" + " or create: /etc/modprobe.d/ipmi.conf with these contents:\n" + " options ipmi_si kipmid_max_busy_us=10\n" + "\n" + " For more information:\n" + " https://github.com/firehol/netdata/wiki/monitoring-IPMI\n" + "\n" + , VERSION + , netdata_update_every + , netdata_do_sel?"enabled":"disabled" + , sdr_cache_directory?sdr_cache_directory:"system default" + , sensor_config_file?sensor_config_file:"system default" + ); + exit(1); + } + else if(i < argc && strcmp("hostname", argv[i]) == 0) { + hostname = strdupz(argv[++i]); + char *s = argv[i]; + // mask it be hidden from the process tree + while(*s) *s++ = 'x'; + if(debug) fprintf(stderr, "freeipmi.plugin: hostname set to '%s'\n", hostname); + continue; + } + else if(i < argc && strcmp("username", argv[i]) == 0) { + username = strdupz(argv[++i]); + char *s = argv[i]; + // mask it be hidden from the process tree + while(*s) *s++ = 'x'; + if(debug) fprintf(stderr, "freeipmi.plugin: username set to '%s'\n", username); + continue; + } + else if(i < argc && strcmp("password", argv[i]) == 0) { + password = strdupz(argv[++i]); + char *s = argv[i]; + // mask it be hidden from the process tree + while(*s) *s++ = 'x'; + if(debug) fprintf(stderr, "freeipmi.plugin: password set to '%s'\n", password); + continue; + } + else if(i < argc && strcmp("sdr-cache-dir", argv[i]) == 0) { + sdr_cache_directory = argv[++i]; + if(debug) fprintf(stderr, "freeipmi.plugin: SDR cache directory set to '%s'\n", sdr_cache_directory); + continue; + } + else if(i < argc && strcmp("sensor-config-file", argv[i]) == 0) { + sensor_config_file = argv[++i]; + if(debug) fprintf(stderr, "freeipmi.plugin: sensor config file set to '%s'\n", sensor_config_file); + continue; + } + + error("freeipmi.plugin: ignoring parameter '%s'", argv[i]); + } + + if(freq > netdata_update_every) + netdata_update_every = freq; + + else if(freq) + error("update frequency %d seconds is too small for IPMI. Using %d.", freq, netdata_update_every); + + + // ------------------------------------------------------------------------ + // initialize IPMI + + struct ipmi_monitoring_ipmi_config ipmi_config; + + if(debug) fprintf(stderr, "freeipmi.plugin: calling _init_ipmi_config()\n"); + + _init_ipmi_config(&ipmi_config); + + if(debug) fprintf(stderr, "freeipmi.plugin: calling ipmi_monitoring_init()\n"); + + if(ipmi_monitoring_init(ipmimonitoring_init_flags, &errnum) < 0) + fatal("ipmi_monitoring_init: %s", ipmi_monitoring_ctx_strerror(errnum)); + + if(debug) fprintf(stderr, "freeipmi.plugin: detecting IPMI minimum update frequency...\n"); + freq = ipmi_detect_speed_secs(&ipmi_config); + if(debug) fprintf(stderr, "freeipmi.plugin: IPMI minimum update frequency was calculated to %d seconds.\n", freq); + + if(netdata_update_every < freq) { + info("enforcing minimum data collection frequency, calculated to %d seconds.", freq); + netdata_update_every = freq; + } + + + // ------------------------------------------------------------------------ + // the main loop + + if(debug) fprintf(stderr, "freeipmi.plugin: starting data collection\n"); + + time_t started_t = now_monotonic_sec(); + + size_t iteration = 0; + usec_t step = netdata_update_every * USEC_PER_SEC; + + heartbeat_t hb; + heartbeat_init(&hb); + for(iteration = 0; 1 ; iteration++) { + usec_t dt = heartbeat_next(&hb, step); + + if(debug && iteration) + fprintf(stderr, "freeipmi.plugin: iteration %zu, dt %llu usec, sensors collected %zu, sensors sent to netdata %zu \n" + , iteration + , dt + , netdata_sensors_collected + , netdata_sensors_updated + ); + + netdata_mark_as_not_updated(); + + if(debug) fprintf(stderr, "freeipmi.plugin: calling ipmi_collect_data()\n"); + if(ipmi_collect_data(&ipmi_config) < 0) + fatal("data collection failed."); + + if(debug) fprintf(stderr, "freeipmi.plugin: calling send_metrics_to_netdata()\n"); + send_metrics_to_netdata(); + fflush(stdout); + + // restart check (14400 seconds) + if(now_monotonic_sec() - started_t > 14400) exit(0); + } +} + +#else // !HAVE_FREEIPMI + +int main(int argc, char **argv) { + fatal("freeipmi.plugin is not compiled."); +} + +#endif // !HAVE_FREEIPMI diff --git a/src/global_statistics.c b/src/global_statistics.c index a698615f4..8575061ac 100644 --- a/src/global_statistics.c +++ b/src/global_statistics.c @@ -10,14 +10,14 @@ volatile struct global_statistics global_statistics = { .compressed_content_size = 0 }; -pthread_mutex_t global_statistics_mutex = PTHREAD_MUTEX_INITIALIZER; +netdata_mutex_t global_statistics_mutex = NETDATA_MUTEX_INITIALIZER; inline void global_statistics_lock(void) { - pthread_mutex_lock(&global_statistics_mutex); + netdata_mutex_lock(&global_statistics_mutex); } inline void global_statistics_unlock(void) { - pthread_mutex_unlock(&global_statistics_mutex); + netdata_mutex_unlock(&global_statistics_mutex); } void finished_web_request_statistics(uint64_t dt, @@ -129,14 +129,22 @@ void global_statistics_charts(void) { getrusage(RUSAGE_THREAD, &thread); getrusage(RUSAGE_SELF, &me); - if (!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_proc_cpu"); +#ifdef __FreeBSD__ + if (!stcpu_thread) stcpu_thread = rrdset_find_localhost("netdata.plugin_freebsd_cpu"); if (!stcpu_thread) { - stcpu_thread = rrdset_create("netdata", "plugin_proc_cpu", NULL, "proc", NULL, - "NetData Proc Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, - RRDSET_TYPE_STACKED); + stcpu_thread = rrdset_create_localhost("netdata", "plugin_freebsd_cpu", NULL, "freebsd", NULL + , "NetData FreeBSD Plugin CPU usage", "milliseconds/s", 132000 + , localhost->rrd_update_every, RRDSET_TYPE_STACKED); +#else + if (!stcpu_thread) stcpu_thread = rrdset_find_localhost("netdata.plugin_proc_cpu"); + if (!stcpu_thread) { + stcpu_thread = rrdset_create_localhost("netdata", "plugin_proc_cpu", NULL, "proc", NULL + , "NetData Proc Plugin CPU usage", "milliseconds/s", 132000 + , localhost->rrd_update_every, RRDSET_TYPE_STACKED); +#endif - rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); - rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); + rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(stcpu_thread); rrddim_set(stcpu_thread, "user", thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec); @@ -145,13 +153,13 @@ void global_statistics_charts(void) { // ---------------------------------------------------------------- - if (!stcpu) stcpu = rrdset_find("netdata.server_cpu"); + if (!stcpu) stcpu = rrdset_find_localhost("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); + stcpu = rrdset_create_localhost("netdata", "server_cpu", NULL, "netdata", NULL, "NetData CPU usage" + , "milliseconds/s", 130000, localhost->rrd_update_every, RRDSET_TYPE_STACKED); - rrddim_add(stcpu, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); - rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); + rrddim_add(stcpu, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(stcpu, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(stcpu); rrddim_set(stcpu, "user", me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec); @@ -160,12 +168,12 @@ void global_statistics_charts(void) { // ---------------------------------------------------------------- - if (!stclients) stclients = rrdset_find("netdata.clients"); + if (!stclients) stclients = rrdset_find_localhost("netdata.clients"); if (!stclients) { - stclients = rrdset_create("netdata", "clients", NULL, "netdata", NULL, "NetData Web Clients", - "connected clients", 130200, rrd_update_every, RRDSET_TYPE_LINE); + stclients = rrdset_create_localhost("netdata", "clients", NULL, "netdata", NULL, "NetData Web Clients" + , "connected clients", 130200, localhost->rrd_update_every, RRDSET_TYPE_LINE); - rrddim_add(stclients, "clients", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(stclients, "clients", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(stclients); rrddim_set(stclients, "clients", gs.connected_clients); @@ -173,12 +181,12 @@ void global_statistics_charts(void) { // ---------------------------------------------------------------- - if (!streqs) streqs = rrdset_find("netdata.requests"); + if (!streqs) streqs = rrdset_find_localhost("netdata.requests"); if (!streqs) { - streqs = rrdset_create("netdata", "requests", NULL, "netdata", NULL, "NetData Web Requests", "requests/s", - 130300, rrd_update_every, RRDSET_TYPE_LINE); + streqs = rrdset_create_localhost("netdata", "requests", NULL, "netdata", NULL, "NetData Web Requests" + , "requests/s", 130300, localhost->rrd_update_every, RRDSET_TYPE_LINE); - rrddim_add(streqs, "requests", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(streqs, "requests", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(streqs); rrddim_set(streqs, "requests", (collected_number) gs.web_requests); @@ -186,13 +194,13 @@ void global_statistics_charts(void) { // ---------------------------------------------------------------- - if (!stbytes) stbytes = rrdset_find("netdata.net"); + if (!stbytes) stbytes = rrdset_find_localhost("netdata.net"); if (!stbytes) { - stbytes = rrdset_create("netdata", "net", NULL, "netdata", NULL, "NetData Network Traffic", "kilobits/s", - 130000, rrd_update_every, RRDSET_TYPE_AREA); + stbytes = rrdset_create_localhost("netdata", "net", NULL, "netdata", NULL, "NetData Network Traffic" + , "kilobits/s", 130000, localhost->rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(stbytes, "in", NULL, 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(stbytes, "out", NULL, -8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(stbytes, "in", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(stbytes, "out", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(stbytes); rrddim_set(stbytes, "in", (collected_number) gs.bytes_received); @@ -201,13 +209,14 @@ void global_statistics_charts(void) { // ---------------------------------------------------------------- - if (!stduration) stduration = rrdset_find("netdata.response_time"); + if (!stduration) stduration = rrdset_find_localhost("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); + stduration = rrdset_create_localhost("netdata", "response_time", NULL, "netdata", NULL + , "NetData API Response Time", "ms/request", 130400, localhost->rrd_update_every + , RRDSET_TYPE_LINE); - rrddim_add(stduration, "average", NULL, 1, 1000, RRDDIM_ABSOLUTE); - rrddim_add(stduration, "max", NULL, 1, 1000, RRDDIM_ABSOLUTE); + rrddim_add(stduration, "average", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(stduration, "max", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(stduration); uint64_t gweb_usec = gs.web_usec; @@ -232,13 +241,13 @@ void global_statistics_charts(void) { // ---------------------------------------------------------------- - if (!stcompression) stcompression = rrdset_find("netdata.compression_ratio"); + if (!stcompression) stcompression = rrdset_find_localhost("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); + stcompression = rrdset_create_localhost("netdata", "compression_ratio", NULL, "netdata", NULL + , "NetData API Responses Compression Savings Ratio", "percentage" + , 130500, localhost->rrd_update_every, RRDSET_TYPE_LINE); - rrddim_add(stcompression, "savings", NULL, 1, 1000, RRDDIM_ABSOLUTE); + rrddim_add(stcompression, "savings", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(stcompression); // since we don't lock here to read the global statistics diff --git a/src/health.c b/src/health.c index 193312eec..46b27db6f 100755..100644 --- a/src/health.c +++ b/src/health.c @@ -1,2588 +1,84 @@ +#define NETDATA_HEALTH_INTERNALS #include "common.h" -#define RRDVAR_MAX_LENGTH 1024 - -struct health_options { - const char *health_default_exec; - const char *health_default_recipient; - const char *log_filename; - size_t log_entries_written; - FILE *log_fp; -}; - -static struct health_options health = { - .health_default_exec = PLUGINS_DIR "/alarm-notify.sh", - .health_default_recipient = "root", - .log_filename = VARLIB_DIR "/health/alarm_log.db", - .log_entries_written = 0, - .log_fp = NULL -}; - -int health_enabled = 1; - -// ---------------------------------------------------------------------------- -// health alarm log load/save -// no need for locking - only one thread is reading / writing the alarms log - -static inline int health_alarm_log_open(void) { - if(health.log_fp) - fclose(health.log_fp); - - health.log_fp = fopen(health.log_filename, "a"); - - if(health.log_fp) { - if (setvbuf(health.log_fp, NULL, _IOLBF, 0) != 0) - error("Health: cannot set line buffering on health log file."); - return 0; - } - - error("Health: cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", health.log_filename); - return -1; -} - -static inline void health_alarm_log_close(void) { - if(health.log_fp) { - fclose(health.log_fp); - health.log_fp = NULL; - } -} - -static inline void health_log_rotate(void) { - static size_t rotate_every = 0; - - if(unlikely(rotate_every == 0)) { - rotate_every = (size_t)config_get_number("health", "rotate log every lines", 2000); - if(rotate_every < 100) rotate_every = 100; - } - - if(unlikely(health.log_entries_written > rotate_every)) { - health_alarm_log_close(); - - char old_filename[FILENAME_MAX + 1]; - snprintfz(old_filename, FILENAME_MAX, "%s.old", health.log_filename); - - if(unlink(old_filename) == -1 && errno != ENOENT) - error("Health: cannot remove old alarms log file '%s'", old_filename); - - if(link(health.log_filename, old_filename) == -1 && errno != ENOENT) - error("Health: cannot move file '%s' to '%s'.", health.log_filename, old_filename); - - if(unlink(health.log_filename) == -1 && errno != ENOENT) - error("Health: cannot remove old alarms log file '%s'", health.log_filename); - - // open it with truncate - health.log_fp = fopen(health.log_filename, "w"); - - if(health.log_fp) - fclose(health.log_fp); - else - error("Health: cannot truncate health log '%s'", health.log_filename); - - health.log_fp = NULL; - - health.log_entries_written = 0; - health_alarm_log_open(); - } -} - -static inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) { - health_log_rotate(); - - if(likely(health.log_fp)) { - if(unlikely(fprintf(health.log_fp - , "%c\t%s" - "\t%08x\t%08x\t%08x\t%08x\t%08x" - "\t%08x\t%08x\t%08x" - "\t%08x\t%08x\t%08x" - "\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" - "\t%d\t%d\t%d\t%d" - "\t%Lf\t%Lf" - "\n" - , (ae->flags & HEALTH_ENTRY_FLAG_SAVED)?'U':'A' - , host->hostname - - , ae->unique_id - , ae->alarm_id - , ae->alarm_event_id - , ae->updated_by_id - , ae->updates_id - - , (uint32_t)ae->when - , (uint32_t)ae->duration - , (uint32_t)ae->non_clear_duration - , (uint32_t)ae->flags - , (uint32_t)ae->exec_run_timestamp - , (uint32_t)ae->delay_up_to_timestamp - - , (ae->name)?ae->name:"" - , (ae->chart)?ae->chart:"" - , (ae->family)?ae->family:"" - , (ae->exec)?ae->exec:"" - , (ae->recipient)?ae->recipient:"" - , (ae->source)?ae->source:"" - , (ae->units)?ae->units:"" - , (ae->info)?ae->info:"" - - , ae->exec_code - , ae->new_status - , ae->old_status - , ae->delay - - , (long double)ae->new_value - , (long double)ae->old_value - ) < 0)) - error("Health: failed to save alarm log entry. Health data may be lost in case of abnormal restart."); - else { - ae->flags |= HEALTH_ENTRY_FLAG_SAVED; - health.log_entries_written++; - } - } -} - -static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *filename) { - static uint32_t max_unique_id = 0, max_alarm_id = 0; - - errno = 0; - - char *s, *buf = mallocz(65536 + 1); - size_t line = 0, len = 0; - ssize_t loaded = 0, updated = 0, errored = 0, duplicate = 0; - - pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock); - - while((s = fgets_trim_len(buf, 65536, fp, &len))) { - health.log_entries_written++; - line++; - - int max_entries = 30, entries = 0; - char *pointers[max_entries]; - - pointers[entries++] = s++; - while(*s) { - if(unlikely(*s == '\t')) { - *s = '\0'; - pointers[entries++] = ++s; - if(entries >= max_entries) { - error("Health: line %zu of file '%s' has more than %d entries. Ignoring excessive entries.", line, filename, max_entries); - break; - } - } - else s++; - } - - if(likely(*pointers[0] == 'U' || *pointers[0] == 'A')) { - ALARM_ENTRY *ae = NULL; - - if(entries < 26) { - error("Health: line %zu of file '%s' should have at least 26 entries, but it has %d. Ignoring it.", line, filename, entries); - errored++; - continue; - } - - // check that we have valid ids - uint32_t unique_id = (uint32_t)strtoul(pointers[2], NULL, 16); - if(!unique_id) { - error("Health: line %zu of file '%s' states alarm entry with invalid unique id %u (%s). Ignoring it.", line, filename, unique_id, pointers[2]); - errored++; - continue; - } - - uint32_t alarm_id = (uint32_t)strtoul(pointers[3], NULL, 16); - if(!alarm_id) { - error("Health: line %zu of file '%s' states alarm entry for invalid alarm id %u (%s). Ignoring it.", line, filename, alarm_id, pointers[3]); - errored++; - continue; - } - - if(unlikely(*pointers[0] == 'A')) { - // make sure it is properly numbered - if(unlikely(host->health_log.alarms && unique_id < host->health_log.alarms->unique_id)) { - error("Health: line %zu of file '%s' has alarm log entry with %u in wrong order. Ignoring it.", line, filename, unique_id); - errored++; - continue; - } - - ae = callocz(1, sizeof(ALARM_ENTRY)); - } - else if(unlikely(*pointers[0] == 'U')) { - // find the original - for(ae = host->health_log.alarms; ae; ae = ae->next) { - if(unlikely(unique_id == ae->unique_id)) { - if(unlikely(*pointers[0] == 'A')) { - error("Health: line %zu of file '%s' adds duplicate alarm log entry with unique id %u. Using the later." - , line, filename, unique_id); - *pointers[0] = 'U'; - duplicate++; - } - break; - } - else if(unlikely(unique_id > ae->unique_id)) { - // no need to continue - // the linked list is sorted - ae = NULL; - break; - } - } - - // if not found, skip this line - if(!ae) { - // error("Health: line %zu of file '%s' updates alarm log entry with unique id %u, but it is not found.", line, filename, unique_id); - continue; - } - } - - // check for a possible host missmatch - //if(strcmp(pointers[1], host->hostname)) - // error("Health: line %zu of file '%s' provides an alarm for host '%s' but this is named '%s'.", line, filename, pointers[1], host->hostname); - - ae->unique_id = unique_id; - ae->alarm_id = alarm_id; - ae->alarm_event_id = (uint32_t)strtoul(pointers[4], NULL, 16); - ae->updated_by_id = (uint32_t)strtoul(pointers[5], NULL, 16); - ae->updates_id = (uint32_t)strtoul(pointers[6], NULL, 16); - - ae->when = (uint32_t)strtoul(pointers[7], NULL, 16); - ae->duration = (uint32_t)strtoul(pointers[8], NULL, 16); - ae->non_clear_duration = (uint32_t)strtoul(pointers[9], NULL, 16); - - ae->flags = (uint32_t)strtoul(pointers[10], NULL, 16); - ae->flags |= HEALTH_ENTRY_FLAG_SAVED; - - ae->exec_run_timestamp = (uint32_t)strtoul(pointers[11], NULL, 16); - ae->delay_up_to_timestamp = (uint32_t)strtoul(pointers[12], NULL, 16); - - if(unlikely(ae->name)) freez(ae->name); - ae->name = strdupz(pointers[13]); - ae->hash_name = simple_hash(ae->name); - - if(unlikely(ae->chart)) freez(ae->chart); - ae->chart = strdupz(pointers[14]); - ae->hash_chart = simple_hash(ae->chart); - - if(unlikely(ae->family)) freez(ae->family); - ae->family = strdupz(pointers[15]); - - if(unlikely(ae->exec)) freez(ae->exec); - ae->exec = strdupz(pointers[16]); - if(!*ae->exec) { freez(ae->exec); ae->exec = NULL; } - - if(unlikely(ae->recipient)) freez(ae->recipient); - ae->recipient = strdupz(pointers[17]); - if(!*ae->recipient) { freez(ae->recipient); ae->recipient = NULL; } - - if(unlikely(ae->source)) freez(ae->source); - ae->source = strdupz(pointers[18]); - if(!*ae->source) { freez(ae->source); ae->source = NULL; } - - if(unlikely(ae->units)) freez(ae->units); - ae->units = strdupz(pointers[19]); - if(!*ae->units) { freez(ae->units); ae->units = NULL; } - - if(unlikely(ae->info)) freez(ae->info); - ae->info = strdupz(pointers[20]); - if(!*ae->info) { freez(ae->info); ae->info = NULL; } - - ae->exec_code = str2i(pointers[21]); - ae->new_status = str2i(pointers[22]); - ae->old_status = str2i(pointers[23]); - ae->delay = str2i(pointers[24]); - - ae->new_value = str2l(pointers[25]); - ae->old_value = str2l(pointers[26]); - - // add it to host if not already there - if(unlikely(*pointers[0] == 'A')) { - ae->next = host->health_log.alarms; - host->health_log.alarms = ae; - loaded++; - } - else updated++; - - if(unlikely(ae->unique_id > max_unique_id)) - max_unique_id = ae->unique_id; - - if(unlikely(ae->alarm_id >= max_alarm_id)) - max_alarm_id = ae->alarm_id; - } - else { - error("Health: line %zu of file '%s' is invalid (unrecognized entry type '%s').", line, filename, pointers[0]); - errored++; - } - } - - pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock); - - freez(buf); - - if(!max_unique_id) max_unique_id = (uint32_t)now_realtime_sec(); - if(!max_alarm_id) max_alarm_id = (uint32_t)now_realtime_sec(); - - host->health_log.next_log_id = max_unique_id + 1; - host->health_log.next_alarm_id = max_alarm_id + 1; - - debug(D_HEALTH, "Health: loaded file '%s' with %zd new alarm entries, updated %zd alarms, errors %zd entries, duplicate %zd", filename, loaded, updated, errored, duplicate); - return loaded; -} - -static inline void health_alarm_log_load(RRDHOST *host) { - health_alarm_log_close(); - - char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s.old", health.log_filename); - FILE *fp = fopen(filename, "r"); - if(!fp) - error("Health: cannot open health file: %s", filename); - else { - health_alarm_log_read(host, fp, filename); - fclose(fp); - } - - health.log_entries_written = 0; - fp = fopen(health.log_filename, "r"); - if(!fp) - error("Health: cannot open health file: %s", health.log_filename); - else { - health_alarm_log_read(host, fp, health.log_filename); - fclose(fp); - } - - health_alarm_log_open(); -} - - -// ---------------------------------------------------------------------------- -// health alarm log management - -static inline void health_alarm_log(RRDHOST *host, - uint32_t alarm_id, uint32_t alarm_event_id, - time_t when, - const char *name, const char *chart, const char *family, - const char *exec, const char *recipient, 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, - int delay -) { - debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id); - - 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(recipient) ae->recipient = strdupz(recipient); - if(source) ae->source = strdupz(source); - if(units) ae->units = strdupz(units); - if(info) ae->info = strdupz(info); - - ae->unique_id = host->health_log.next_log_id++; - ae->alarm_id = alarm_id; - ae->alarm_event_id = alarm_event_id; - 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; - ae->delay = delay; - ae->delay_up_to_timestamp = when + delay; - - 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->alarm_id == ae->alarm_id) { - if(!(t->flags & HEALTH_ENTRY_FLAG_UPDATED) && !t->updated_by_id) { - t->flags |= HEALTH_ENTRY_FLAG_UPDATED; - t->updated_by_id = ae->unique_id; - ae->updates_id = t->unique_id; - - 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; - - health_alarm_log_save(host, t); - } - - // no need to continue - break; - } - } - pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock); - - health_alarm_log_save(host, ae); -} - -// ---------------------------------------------------------------------------- -// 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) { - debug(D_VARIABLES, "Deleting variable '%s'", rv->name); - if(unlikely(!rrdvar_index_del(tree, rv))) - error("Attempted to delete variable '%s' from host '%s', but it is not found.", rv->name, host->hostname); - } - - freez(rv->name); - freez(rv); -} - -static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *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 { - debug(D_VARIABLES, "Variable '%s' is already found in scope '%s'.", variable, scope); - - // already exists - freez(variable); - - // this is important - // it must return NULL - not the existing variable - or double-free will happen - rv = NULL; - } - - return rv; -} - -// ---------------------------------------------------------------------------- -// CUSTOM VARIABLES - -RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name) { - calculated_number *v = callocz(1, sizeof(calculated_number)); - *v = NAN; - RRDVAR *rv = rrdvar_create_and_index("host", &host->variables_root_index, name, RRDVAR_TYPE_CALCULATED_ALLOCATED, v); - if(unlikely(!rv)) { - free(v); - error("Requested variable '%s' already exists - possibly 2 plugins will be updating it at the same time", name); - - char *variable = strdupz(name); - rrdvar_fix_name(variable); - uint32_t hash = simple_hash(variable); - - rv = rrdvar_index_find(&host->variables_root_index, variable, hash); - } - - return rv; -} - -void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name) { - char *variable = strdupz(name); - rrdvar_fix_name(variable); - uint32_t hash = simple_hash(variable); - - RRDVAR *rv = rrdvar_index_find(&host->variables_root_index, variable, hash); - freez(variable); - - if(!rv) { - error("Attempted to remove variable '%s' from host '%s', but it does not exist.", name, host->hostname); - return; - } - - if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) { - error("Attempted to remove variable '%s' from host '%s', but it does not a custom allocated variable.", name, host->hostname); - return; - } - - if(!rrdvar_index_del(&host->variables_root_index, rv)) { - error("Attempted to remove variable '%s' from host '%s', but it cannot be found.", name, host->hostname); - return; - } - - freez(rv->name); - freez(rv->value); - freez(rv); -} - -void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value) { - if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) - error("requested to set variable '%s' to value " CALCULATED_NUMBER_FORMAT " but the variable is not a custom one.", rv->name, value); - else { - calculated_number *v = rv->value; - *v = value; - } -} - -// ---------------------------------------------------------------------------- -// RRDVAR lookup - -static calculated_number rrdvar2number(RRDVAR *rv) { - switch(rv->type) { - case RRDVAR_TYPE_CALCULATED_ALLOCATED: - 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; - } -} - -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; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RRDVAR to JSON - -struct variable2json_helper { - BUFFER *buf; - size_t counter; -}; - -static int single_variable2json(void *entry, void *data) { - struct variable2json_helper *helper = (struct variable2json_helper *)data; - RRDVAR *rv = (RRDVAR *)entry; - calculated_number value = rrdvar2number(rv); - - if(unlikely(isnan(value) || isinf(value))) - buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name); - else - buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value); - - helper->counter++; - - return 0; -} - -void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) { - struct variable2json_helper helper = { - .buf = buf, - .counter = 0 - }; - - buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", st->id, st->name, st->context); - avl_traverse_lock(&st->variables_root_index, single_variable2json, (void *)&helper); - buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family); - helper.counter = 0; - avl_traverse_lock(&st->rrdfamily->variables_root_index, single_variable2json, (void *)&helper); - buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", st->rrdhost->hostname); - helper.counter = 0; - avl_traverse_lock(&st->rrdhost->variables_root_index, single_variable2json, (void *)&helper); - buffer_strcat(buf, "\n\t}\n}\n"); -} - - -// ---------------------------------------------------------------------------- -// RRDDIMVAR management -// DIMENSION VARIABLES - -#define RRDDIMVAR_ID_MAX 1024 - -static inline void rrddimvar_free_variables(RRDDIMVAR *rs) { - RRDDIM *rd = rs->rrddim; - RRDSET *st = rd->rrdset; - - // CHART VARIABLES FOR THIS DIMENSION - - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_id); - rs->var_local_id = NULL; - - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_name); - rs->var_local_name = NULL; - - // FAMILY VARIABLES FOR THIS DIMENSION - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_id); - rs->var_family_id = NULL; - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); - rs->var_family_name = NULL; - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextid); - rs->var_family_contextid = NULL; - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextname); - rs->var_family_contextname = NULL; - - // HOST VARIABLES FOR THIS DIMENSION - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidid); - rs->var_host_chartidid = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidname); - rs->var_host_chartidname = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnameid); - rs->var_host_chartnameid = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnamename); - rs->var_host_chartnamename = NULL; - - // KEYS - - freez(rs->key_id); - rs->key_id = NULL; - - freez(rs->key_name); - rs->key_name = NULL; - - freez(rs->key_fullidid); - rs->key_fullidid = NULL; - - freez(rs->key_fullidname); - rs->key_fullidname = NULL; - - freez(rs->key_contextid); - rs->key_contextid = NULL; - - freez(rs->key_contextname); - rs->key_contextname = NULL; - - freez(rs->key_fullnameid); - rs->key_fullnameid = NULL; - - freez(rs->key_fullnamename); - rs->key_fullnamename = NULL; -} - -static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { - rrddimvar_free_variables(rs); - - RRDDIM *rd = rs->rrddim; - RRDSET *st = rd->rrdset; - - char buffer[RRDDIMVAR_ID_MAX + 1]; - - // KEYS - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix); - rs->key_id = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix); - rs->key_name = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_id); - rs->key_fullidid = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_name); - rs->key_fullidname = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_id); - rs->key_contextid = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_name); - rs->key_contextname = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_id); - rs->key_fullnameid = strdupz(buffer); - - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_name); - rs->key_fullnamename = strdupz(buffer); - - // CHART VARIABLES FOR THIS DIMENSION - // ----------------------------------- - // - // dimensions are available as: - // - $id - // - $name - - rs->var_local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_id, rs->type, rs->value); - rs->var_local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_name, rs->type, rs->value); - - // FAMILY VARIABLES FOR THIS DIMENSION - // ----------------------------------- - // - // dimensions are available as: - // - $id (only the first, when multiple overlap) - // - $name (only the first, when multiple overlap) - // - $chart-context.id - // - $chart-context.name - - rs->var_family_id = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_id, rs->type, rs->value); - rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_name, rs->type, rs->value); - rs->var_family_contextid = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextid, rs->type, rs->value); - rs->var_family_contextname = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextname, rs->type, rs->value); - - // HOST VARIABLES FOR THIS DIMENSION - // ----------------------------------- - // - // dimensions are available as: - // - $chart-id.id - // - $chart-id.name - // - $chart-name.id - // - $chart-name.name - - rs->var_host_chartidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidid, rs->type, rs->value); - rs->var_host_chartidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidname, rs->type, rs->value); - rs->var_host_chartnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnameid, rs->type, rs->value); - rs->var_host_chartnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnamename, rs->type, rs->value); -} - -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 = ""; - - RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR)); - - rs->prefix = strdupz(prefix); - rs->suffix = strdupz(suffix); - - rs->type = type; - rs->value = value; - rs->options = options; - rs->rrddim = rd; - - rs->next = rd->variables; - rd->variables = rs; - - rrddimvar_create_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; - rrddimvar_create_variables(rs); - } -} - -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); - - rrddimvar_free_variables(rs); - - 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->key_name, st->id, rd->id); - else t->next = rs->next; - } - - freez(rs->prefix); - freez(rs->suffix); - freez(rs); -} - -// ---------------------------------------------------------------------------- -// RRDSETVAR management -// CHART VARIABLES - -static inline void rrdsetvar_free_variables(RRDSETVAR *rs) { - RRDSET *st = rs->rrdset; - - // CHART - - rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local); - rs->var_local = NULL; - - // FAMILY - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family); - rs->var_family = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host); - rs->var_host = NULL; - - // HOST - - rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); - rs->var_family_name = NULL; - - rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_name); - rs->var_host_name = NULL; - - // KEYS - - freez(rs->key_fullid); - rs->key_fullid = NULL; - - freez(rs->key_fullname); - rs->key_fullname = NULL; -} - -static inline void rrdsetvar_create_variables(RRDSETVAR *rs) { - rrdsetvar_free_variables(rs); - - RRDSET *st = rs->rrdset; - - // KEYS - - char buffer[RRDVAR_MAX_LENGTH + 1]; - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rs->variable); - rs->key_fullid = strdupz(buffer); - - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable); - rs->key_fullname = strdupz(buffer); - - // CHART - - rs->var_local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->type, rs->value); - - // FAMILY - - rs->var_family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullid, rs->type, rs->value); - rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullname, rs->type, rs->value); - - // HOST - - rs->var_host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullid, rs->type, rs->value); - rs->var_host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullname, rs->type, rs->value); - -} - -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)); - - rs->variable = strdupz(variable); - rs->type = type; - rs->value = value; - rs->options = options; - rs->rrdset = st; - - rs->next = st->variables; - st->variables = rs; - - rrdsetvar_create_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); - - RRDSETVAR *rs, *next = st->variables; - while((rs = next)) { - next = rs->next; - rrdsetvar_create_variables(rs); - } - - 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); - - 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->key_fullname, st->id); - else t->next = rs->next; - } - - rrdsetvar_free_variables(rs); - - freez(rs->variable); - freez(rs); -} - -// ---------------------------------------------------------------------------- -// RRDCALC management - -inline const char *rrdcalc_status2string(int status) { - switch(status) { - case RRDCALC_STATUS_REMOVED: - return "REMOVED"; - - case RRDCALC_STATUS_UNDEFINED: - return "UNDEFINED"; - - case RRDCALC_STATUS_UNINITIALIZED: - return "UNINITIALIZED"; - - 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: - error("Unknown alarm status %d", status); - 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 = now_realtime_sec(); - rc->rrdset = st; - - rc->rrdset_next = st->alarms; - rc->rrdset_prev = NULL; - - if(rc->rrdset_next) - rc->rrdset_next->rrdset_prev = rc; - - 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); - - { - time_t now = now_realtime_sec(); - health_alarm_log(st->rrdhost, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, rc->recipient, now - rc->last_status_change, rc->old_value, rc->value, rc->status, RRDCALC_STATUS_UNINITIALIZED, rc->source, rc->units, rc->info, 0); - } -} - -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(unlikely(rc->rrdset)) - continue; - - if(unlikely(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) { - debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); - error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); - return; - } - - { - time_t now = now_realtime_sec(); - health_alarm_log(st->rrdhost, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, rc->recipient, now - rc->last_status_change, rc->old_value, rc->value, rc->status, RRDCALC_STATUS_REMOVED, rc->source, rc->units, rc->info, 0); - } - - 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(unlikely(rc->hash == hash && !strcmp(rc->name, name))) - return rc; - } - - return NULL; -} - -static inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name) { - RRDCALC *rc; - - if(unlikely(!chart)) { - error("attempt to find RRDCALC '%s' without giving a chart name", name); - return 1; - } - - if(unlikely(!hash_chart)) hash_chart = simple_hash(chart); - if(unlikely(!hash_name)) hash_name = simple_hash(name); - - // make sure it does not already exist - for(rc = host->alarms; rc ; rc = rc->next) { - if (unlikely(rc->chart && rc->hash == hash_name && rc->hash_chart == hash_chart && !strcmp(name, rc->name) && !strcmp(chart, rc->chart))) { - debug(D_HEALTH, "Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); - error("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); - return 1; - } - } - - return 0; -} - -static inline uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id) { - if(chart && name) { - uint32_t hash_chart = simple_hash(chart); - uint32_t hash_name = simple_hash(name); - - // re-use old IDs, by looking them up in the alarm log - ALARM_ENTRY *ae; - for(ae = host->health_log.alarms; ae ;ae = ae->next) { - if(unlikely(ae->hash_name == hash_name && ae->hash_chart == hash_chart && !strcmp(name, ae->name) && !strcmp(chart, ae->chart))) { - if(next_event_id) *next_event_id = ae->alarm_event_id + 1; - return ae->alarm_id; - } - } - } - - return host->health_log.next_alarm_id++; -} - -static inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) { - rrdhost_check_rdlock(host); - - if(rc->calculation) { - rc->calculation->status = &rc->status; - 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->status = &rc->status; - 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->status = &rc->status; - 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 - if(likely(host->alarms)) { - // append it - RRDCALC *t; - for(t = host->alarms; t && t->next ; t = t->next) ; - t->next = rc; - } - else { - 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 RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart) { - - debug(D_HEALTH, "Health creating dynamic alarm (from template) '%s.%s'", chart, rt->name); - - if(rrdcalc_exists(host, chart, rt->name, 0, 0)) - return NULL; - - RRDCALC *rc = callocz(1, sizeof(RRDCALC)); - rc->next_event_id = 1; - rc->id = rrdcalc_get_unique_id(host, chart, rt->name, &rc->next_event_id); - rc->name = strdupz(rt->name); - rc->hash = simple_hash(rc->name); - rc->chart = strdupz(chart); - rc->hash_chart = simple_hash(rc->chart); - - if(rt->dimensions) rc->dimensions = strdupz(rt->dimensions); - - rc->green = rt->green; - rc->red = rt->red; - rc->value = NAN; - rc->old_value = NAN; - - rc->delay_up_duration = rt->delay_up_duration; - rc->delay_down_duration = rt->delay_down_duration; - rc->delay_max_duration = rt->delay_max_duration; - rc->delay_multiplier = rt->delay_multiplier; - - rc->group = rt->group; - rc->after = rt->after; - rc->before = rt->before; - rc->update_every = rt->update_every; - rc->options = rt->options; - - if(rt->exec) rc->exec = strdupz(rt->exec); - if(rt->recipient) rc->recipient = strdupz(rt->recipient); - if(rt->source) rc->source = strdupz(rt->source); - if(rt->units) rc->units = strdupz(rt->units); - if(rt->info) rc->info = strdupz(rt->info); - - if(rt->calculation) { - rc->calculation = expression_parse(rt->calculation->source, NULL, NULL); - if(!rc->calculation) - error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, rt->name, rt->calculation->source); - } - if(rt->warning) { - rc->warning = expression_parse(rt->warning->source, NULL, NULL); - if(!rc->warning) - error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, rt->name, rt->warning->source); - } - if(rt->critical) { - rc->critical = expression_parse(rt->critical->source, NULL, NULL); - if(!rc->critical) - error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, rt->name, rt->critical->source); - } - - debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', recipient '%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', delay up %d, delay down %d, delay max %d, delay_multiplier %f", - (rc->chart)?rc->chart:"NOCHART", - rc->name, - (rc->exec)?rc->exec:"DEFAULT", - (rc->recipient)?rc->recipient:"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, - rc->delay_up_duration, - rc->delay_down_duration, - rc->delay_max_duration, - rc->delay_multiplier - ); - - 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(unlikely(rc == host->alarms)) - host->alarms = rc->next; - - else if(likely(host->alarms)) { - RRDCALC *t, *last = host->alarms; - for(t = last->next; t && t != rc; last = t, t = t->next) ; - if(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->recipient); - freez(rc->source); - freez(rc->units); - freez(rc->info); - freez(rc); -} +int default_health_enabled = 1; // ---------------------------------------------------------------------------- -// 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) - && (!rt->family_pattern || simple_pattern_matches(rt->family_pattern, st->family))) { - RRDCALC *rc = rrdcalc_create(st->rrdhost, rt, st->id); - if(unlikely(!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); -#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->family_match); - simple_pattern_free(rt->family_pattern); - - freez(rt->name); - freez(rt->exec); - freez(rt->recipient); - 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_FAMILIES_KEY "families" -#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_RECIPIENT_KEY "to" -#define HEALTH_UNITS_KEY "units" -#define HEALTH_INFO_KEY "info" -#define HEALTH_DELAY_KEY "delay" - -static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) { - if(!rc->chart) { - error("Health configuration for alarm '%s' does not have a chart", rc->name); - return 0; - } +// health initialization - 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; - } - - if (rrdcalc_exists(host, rc->chart, rc->name, rc->hash_chart, rc->hash)) - return 0; - - rc->id = rrdcalc_get_unique_id(&localhost, rc->chart, rc->name, &rc->next_event_id); - - debug(D_HEALTH, "Health configuration adding alarm '%s.%s' (%u): exec '%s', recipient '%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', delay up %d, delay down %d, delay max %d, delay_multiplier %f", - rc->chart?rc->chart:"NOCHART", - rc->name, - rc->id, - (rc->exec)?rc->exec:"DEFAULT", - (rc->recipient)?rc->recipient:"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, - rc->delay_up_duration, - rc->delay_down_duration, - rc->delay_max_duration, - rc->delay_multiplier - ); - - rrdcalc_create_part2(host, rc); - return 1; -} - -static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) { - if(unlikely(!rt->context)) { - error("Health configuration for template '%s' does not have a context", rt->name); - return 0; - } - - if(unlikely(!rt->update_every)) { - error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rt->name); - return 0; - } - - if(unlikely(!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, *last = NULL; - for (t = host->templates; t ; last = t, t = t->next) { - if(unlikely(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', recipient '%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', delay up %d, delay down %d, delay max %d, delay_multiplier %f", - rt->name, - (rt->context)?rt->context:"NONE", - (rt->exec)?rt->exec:"DEFAULT", - (rt->recipient)?rt->recipient:"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->delay_up_duration, - rt->delay_down_duration, - rt->delay_max_duration, - rt->delay_multiplier - ); - - if(likely(last)) { - last->next = rt; - } - else { - 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_delay( - size_t line, const char *path, const char *file, char *string, - int *delay_up_duration, - int *delay_down_duration, - int *delay_max_duration, - float *delay_multiplier) { - - char given_up = 0; - char given_down = 0; - char given_max = 0; - char given_multiplier = 0; - - char *s = string; - while(*s) { - char *key = s; - - while(*s && !isspace(*s)) s++; - while(*s && isspace(*s)) *s++ = '\0'; - - if(!*key) break; - - char *value = s; - while(*s && !isspace(*s)) s++; - while(*s && isspace(*s)) *s++ = '\0'; - - if(!strcasecmp(key, "up")) { - if (!health_parse_duration(value, delay_up_duration)) { - error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword", - line, path, file, value, key); - } - else given_up = 1; - } - else if(!strcasecmp(key, "down")) { - if (!health_parse_duration(value, delay_down_duration)) { - error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword", - line, path, file, value, key); - } - else given_down = 1; - } - else if(!strcasecmp(key, "multiplier")) { - *delay_multiplier = strtof(value, NULL); - if(isnan(*delay_multiplier) || isinf(*delay_multiplier) || islessequal(*delay_multiplier, 0)) { - error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword", - line, path, file, value, key); - } - else given_multiplier = 1; - } - else if(!strcasecmp(key, "max")) { - if (!health_parse_duration(value, delay_max_duration)) { - error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword", - line, path, file, value, key); - } - else given_max = 1; - } - else { - error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'", - line, path, file, key); - } - } - - if(!given_up) - *delay_up_duration = 0; - - if(!given_down) - *delay_down_duration = 0; - - if(!given_multiplier) - *delay_multiplier = 1.0; - - if(!given_max) { - if((*delay_max_duration) < (*delay_up_duration) * (*delay_multiplier)) - *delay_max_duration = (*delay_up_duration) * (*delay_multiplier); - - if((*delay_max_duration) < (*delay_down_duration) * (*delay_multiplier)) - *delay_max_duration = (*delay_down_duration) * (*delay_multiplier); - } - - 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 *tabs2spaces(char *s) { - char *t = s; - while(*t) { - if(unlikely(*t == '\t')) *t = ' '; - t++; - } - - return s; -} - -static inline char *health_source_file(size_t line, const char *path, const char *filename) { +inline char *health_config_dir(void) { 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_families = 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, hash_recipient = 0, hash_delay = 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_families = simple_uhash(HEALTH_FAMILIES_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); - hash_recipient = simple_hash(HEALTH_RECIPIENT_KEY); - hash_delay = simple_uhash(HEALTH_DELAY_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++; - s = trim(buffer); - if(!s) continue; - - 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; - } - - 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->next_event_id = 1; - rc->name = tabs2spaces(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; - rc->delay_multiplier = 1.0; - - 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 = tabs2spaces(strdupz(value)); - rt->hash_name = simple_hash(rt->name); - rt->source = health_source_file(line, path, filename); - rt->green = NAN; - rt->red = NAN; - rt->delay_multiplier = 1.0; - - 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)) - error("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 = tabs2spaces(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)) - error("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) { - error("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) { - error("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)) - error("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 = tabs2spaces(strdupz(value)); - } - else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) { - if(rc->recipient) { - if(strcmp(rc->recipient, value)) - error("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->recipient, value, value); - - freez(rc->recipient); - } - rc->recipient = tabs2spaces(strdupz(value)); - } - else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) { - if(rc->units) { - if(strcmp(rc->units, value)) - error("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 = tabs2spaces(strdupz(value)); - strip_quotes(rc->units); - } - else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) { - if(rc->info) { - if(strcmp(rc->info, value)) - error("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 = tabs2spaces(strdupz(value)); - strip_quotes(rc->info); - } - else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) { - health_parse_delay(line, path, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier); - } - 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)) - error("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 = tabs2spaces(strdupz(value)); - rt->hash_context = simple_hash(rt->context); - } - else if(hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) { - freez(rt->family_match); - simple_pattern_free(rt->family_pattern); - - rt->family_match = tabs2spaces(strdupz(value)); - rt->family_pattern = simple_pattern_create(rt->family_match, SIMPLE_PATTERN_EXACT); - } - 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)) - error("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) { - error("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) { - error("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)) - error("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 = tabs2spaces(strdupz(value)); - } - else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) { - if(rt->recipient) { - if(strcmp(rt->recipient, value)) - error("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->recipient, value, value); - - freez(rt->recipient); - } - rt->recipient = tabs2spaces(strdupz(value)); - } - else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) { - if(rt->units) { - if(strcmp(rt->units, value)) - error("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 = tabs2spaces(strdupz(value)); - strip_quotes(rt->units); - } - else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) { - if(rt->info) { - if(strcmp(rt->info, value)) - error("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 = tabs2spaces(strdupz(value)); - strip_quotes(rt->info); - } - else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) { - health_parse_delay(line, path, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier); - } - 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') - )) { - debug(D_HEALTH, "Ignoring directory '%s'", de->d_name); - 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 || de->d_type == DT_UNKNOWN) && - len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) { - health_readfile(path, de->d_name); - } - - else debug(D_HEALTH, "Ignoring file '%s'", 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); + snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_config_dir); + return config_get(CONFIG_SECTION_HEALTH, "health configuration directory", buffer); } void health_init(void) { debug(D_HEALTH, "Health configuration initializing"); - if(!(health_enabled = config_get_boolean("health", "enabled", 1))) { + if(!(default_health_enabled = config_get_boolean(CONFIG_SECTION_HEALTH, "enabled", 1))) { debug(D_HEALTH, "Health is disabled."); return; } - - char *pathname = config_get("health", "health db directory", VARLIB_DIR "/health"); - if(mkdir(pathname, 0770) == -1 && errno != EEXIST) - fatal("Cannot create directory '%s'.", pathname); - - char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/health-log.db", pathname); - health.log_filename = config_get("health", "health db file", filename); - - health_alarm_log_load(&localhost); - health_alarm_log_open(); - - char *path = health_config_dir(); - - { - char buffer[FILENAME_MAX + 1]; - snprintfz(buffer, FILENAME_MAX, "%s/alarm-notify.sh", config_get("global", "plugins directory", PLUGINS_DIR)); - health.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 < 10) { - 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, RRDHOST *host) { - buffer_sprintf(wb, "\n\t{\n" - "\t\t\"hostname\": \"%s\",\n" - "\t\t\"unique_id\": %u,\n" - "\t\t\"alarm_id\": %u,\n" - "\t\t\"alarm_event_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\": %lu,\n" - "\t\t\"exec_failed\": %s,\n" - "\t\t\"exec\": \"%s\",\n" - "\t\t\"recipient\": \"%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" - "\t\t\"delay\": %d,\n" - "\t\t\"delay_up_to_timestamp\": %lu,\n" - "\t\t\"updated_by_id\": %u,\n" - "\t\t\"updates_id\": %u,\n", - host->hostname, - ae->unique_id, - ae->alarm_id, - ae->alarm_event_id, - ae->name, - ae->chart, - ae->family, - (ae->flags & HEALTH_ENTRY_FLAG_PROCESSED)?"true":"false", - (ae->flags & HEALTH_ENTRY_FLAG_UPDATED)?"true":"false", - (unsigned long)ae->exec_run_timestamp, - (ae->flags & HEALTH_ENTRY_FLAG_EXEC_FAILED)?"true":"false", - ae->exec?ae->exec:health.health_default_exec, - ae->recipient?ae->recipient:health.health_default_recipient, - 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), - ae->delay, - (unsigned long)ae->delay_up_to_timestamp, - ae->updated_by_id, - ae->updates_id - ); - - 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, uint32_t after) { - 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(ae->unique_id > after) { - if(likely(count)) buffer_strcat(wb, ","); - health_alarm_entry2json_nolock(wb, ae, host); - } - } - - 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\"id\": %lu,\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\"recipient\": \"%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" - "\t\t\t\"delay_up_duration\": %d,\n" - "\t\t\t\"delay_down_duration\": %d,\n" - "\t\t\t\"delay_max_duration\": %d,\n" - "\t\t\t\"delay_multiplier\": %f,\n" - "\t\t\t\"delay\": %d,\n" - "\t\t\t\"delay_up_to_timestamp\": %lu,\n" - , rc->chart, rc->name - , (unsigned long)rc->id - , rc->name - , rc->chart - , (rc->rrdset && rc->rrdset->family)?rc->rrdset->family:"" - , (rc->rrdset)?"true":"false" - , rc->exec?rc->exec:health.health_default_exec - , rc->recipient?rc->recipient:health.health_default_recipient - , 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 - , rc->delay_up_duration - , rc->delay_down_duration - , rc->delay_max_duration - , rc->delay_multiplier - , rc->delay_last - , (unsigned long)rc->delay_up_to_timestamp - ); - - 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_sprintf(wb, "{\n\t\"hostname\": \"%s\"," - "\n\t\"latest_alarm_log_unique_id\": %u," - "\n\t\"status\": %s," - "\n\t\"now\": %lu," - "\n\t\"alarms\": {\n", - host->hostname, - (host->health_log.next_log_id > 0)?(host->health_log.next_log_id - 1):0, - health_enabled?"true":"false", - (unsigned long)now_realtime_sec()); - - RRDCALC *rc; - for(i = 0, rc = host->alarms; rc ; rc = rc->next) { - if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) - continue; - - if(likely(!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); +// re-load health configuration - buffer_strcat(wb, "\n\t}\n}\n"); - rrdhost_unlock(&localhost); -} +void health_reload_host(RRDHOST *host) { + if(unlikely(!host->health_enabled)) + return; + char *path = health_config_dir(); -// ---------------------------------------------------------------------------- -// re-load health configuration + // free all running alarms + rrdhost_wrlock(host); -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(); - // free all running alarms - rrdhost_rwlock(&localhost); - health_free_all_nolock(&localhost); - rrdhost_unlock(&localhost); + rrdhost_unlock(host); // invalidate all previous entries in the alarm log ALARM_ENTRY *t; - for(t = localhost.health_log.alarms ; t ; t = t->next) { + for(t = host->health_log.alarms ; t ; t = t->next) { if(t->new_status != RRDCALC_STATUS_REMOVED) t->flags |= HEALTH_ENTRY_FLAG_UPDATED; } + rrdhost_rdlock(host); // reset all thresholds to all charts RRDSET *st; - for(st = localhost.rrdset_root; st ; st = st->next) { + rrdset_foreach_read(st, host) { st->green = NAN; st->red = NAN; } + rrdhost_unlock(host); // load the new alarms - rrdhost_rwlock(&localhost); - health_readdir(path); - rrdhost_unlock(&localhost); + rrdhost_wrlock(host); + health_readdir(host, path); // link the loaded alarms to their charts - for(st = localhost.rrdset_root; st ; st = st->next) { - rrdhost_rwlock(&localhost); - + rrdset_foreach_write(st, host) { rrdsetcalc_link_matching(st); rrdcalctemplate_link_matching(st); - - rrdhost_unlock(&localhost); } + + rrdhost_unlock(host); +} + +void health_reload(void) { + + rrd_rdlock(); + + RRDHOST *host; + rrdhost_foreach_read(host) + health_reload_host(host); + + rrd_unlock(); } // ---------------------------------------------------------------------------- @@ -2601,12 +97,21 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { if(unlikely(ae->new_status < RRDCALC_STATUS_CLEAR)) { // do not send notifications for internal statuses + debug(D_HEALTH, "Health not sending notification for alarm '%s.%s' status %s (internal statuses)", ae->chart, ae->name, rrdcalc_status2string(ae->new_status)); + goto done; + } + + if(unlikely(ae->new_status <= RRDCALC_STATUS_CLEAR && (ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION))) { + // do not send notifications for disabled statuses + debug(D_HEALTH, "Health not sending notification for alarm '%s.%s' status %s (it has no-clear-notification enabled)", ae->chart, ae->name, rrdcalc_status2string(ae->new_status)); + // mark it as run, so that we will send the same alarm if it happens again goto done; } // find the previous notification for the same alarm // which we have run the exec script - { + // exception: alarms with HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION set + if(likely(!(ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION))) { uint32_t id = ae->alarm_id; ALARM_ENTRY *t; for(t = ae->next; t ; t = t->next) { @@ -2637,13 +142,10 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { static char command_to_run[ALARM_EXEC_COMMAND_LENGTH + 1]; pid_t command_pid; - const char *exec = ae->exec; - if(!exec) exec = health.health_default_exec; + const char *exec = (ae->exec) ? ae->exec : host->health_default_exec; + const char *recipient = (ae->recipient) ? ae->recipient : host->health_default_recipient; - const char *recipient = ae->recipient; - if(!recipient) recipient = health.health_default_recipient; - - snprintfz(command_to_run, ALARM_EXEC_COMMAND_LENGTH, "exec %s '%s' '%s' '%u' '%u' '%u' '%lu' '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s'", + snprintfz(command_to_run, ALARM_EXEC_COMMAND_LENGTH, "exec %s '%s' '%s' '%u' '%u' '%u' '%lu' '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s' '%s' '%s'", exec, recipient, host->hostname, @@ -2662,7 +164,9 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { (uint32_t)ae->duration, (uint32_t)ae->non_clear_duration, ae->units?ae->units:"", - ae->info?ae->info:"" + ae->info?ae->info:"", + ae->new_value_string, + ae->old_value_string ); ae->flags |= HEALTH_ENTRY_FLAG_EXEC_RUN; @@ -2704,7 +208,7 @@ static inline void health_alarm_log_process(RRDHOST *host) { uint32_t first_waiting = (host->health_log.alarms)?host->health_log.alarms->unique_id:0; time_t now = now_realtime_sec(); - pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock); + netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock); ALARM_ENTRY *ae; for(ae = host->health_log.alarms; ae && ae->unique_id >= stop_at_id ; ae = ae->next) { @@ -2724,13 +228,13 @@ static inline void health_alarm_log_process(RRDHOST *host) { // remember this for the next iteration stop_at_id = first_waiting; - pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock); + netdata_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); + netdata_rwlock_wrlock(&host->health_log.alarm_log_rwlock); ALARM_ENTRY *last = NULL; unsigned int count = host->health_log.max * 2 / 3; @@ -2746,21 +250,13 @@ static inline void health_alarm_log_process(RRDHOST *host) { ALARM_ENTRY *t = ae->next; - freez(ae->name); - freez(ae->chart); - freez(ae->family); - freez(ae->exec); - freez(ae->recipient); - freez(ae->source); - freez(ae->units); - freez(ae->info); - freez(ae); + health_alarm_log_free_one_nochecks_nounlink(ae); ae = t; host->health_log.count--; } - pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock); + netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); } static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) { @@ -2785,6 +281,16 @@ static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) return 0; } + if(unlikely(rrdset_flag_check(rc->rrdset, RRDSET_FLAG_OBSOLETE))) { + debug(D_HEALTH, "Health not running alarm '%s.%s'. The chart has been marked as obsolete", rc->chart?rc->chart:"NOCHART", rc->name); + return 0; + } + + if(unlikely(!rrdset_flag_check(rc->rrdset, RRDSET_FLAG_ENABLED))) { + debug(D_HEALTH, "Health not running alarm '%s.%s'. The chart is not enabled", rc->chart?rc->chart:"NOCHART", rc->name); + return 0; + } + if(unlikely(!rc->rrdset->last_collected_time.tv_sec || rc->rrdset->counter_done < 2)) { debug(D_HEALTH, "Health not running alarm '%s.%s'. Chart is not fully collected yet.", rc->chart?rc->chart:"NOCHART", rc->name); return 0; @@ -2828,299 +334,396 @@ void *health_main(void *ptr) { 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); + int min_run_every = (int)config_get_number(CONFIG_SECTION_HEALTH, "run at least every seconds", 10); if(min_run_every < 1) min_run_every = 1; BUFFER *wb = buffer_create(100); + time_t now = now_realtime_sec(); + time_t now_boottime = now_boottime_sec(); + time_t last_now = now; + time_t last_now_boottime = now_boottime; + time_t hibernation_delay = config_get_number(CONFIG_SECTION_HEALTH, "postpone alarms during hibernation for seconds", 60); + unsigned int loop = 0; - while(health_enabled && !netdata_exit) { + while(!netdata_exit) { loop++; debug(D_HEALTH, "Health monitoring iteration no %u started", loop); - int oldstate, runnable = 0; - time_t now = now_realtime_sec(); + int oldstate, runnable = 0, apply_hibernation_delay = 0; time_t next_run = now + min_run_every; RRDCALC *rc; + // detect if boottime and realtime have twice the difference + // in which case we assume the system was just waken from hibernation + if(unlikely(now - last_now > 2 * (now_boottime - last_now_boottime))) + apply_hibernation_delay = 1; + + last_now = now; + last_now_boottime = now_boottime; + if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0)) error("Cannot set pthread cancel state to DISABLE."); - rrdhost_rdlock(&localhost); + rrd_rdlock(); - // 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))) { - if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE)) - rc->rrdcalc_flags &= ~RRDCALC_FLAG_RUNNABLE; + RRDHOST *host; + rrdhost_foreach_read(host) { + if(unlikely(!host->health_enabled)) continue; - } - - runnable++; - rc->old_value = rc->value; - rc->rrdcalc_flags |= RRDCALC_FLAG_RUNNABLE; - // 1. if there is database lookup, do it - // 2. if there is calculation expression, run it + if(unlikely(apply_hibernation_delay)) { - if (unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) { - /* time_t old_db_timestamp = rc->db_before; */ - int value_is_null = 0; + info("Postponing alarm checks for %ld seconds, on host '%s', due to boottime discrepancy (realtime dt: %ld, boottime dt: %ld)." + , hibernation_delay + , host->hostname + , (long)(now - last_now) + , (long)(now_boottime - last_now_boottime) + ); - 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); + host->health_delay_up_to = now + hibernation_delay; + } - if (unlikely(ret != 200)) { - // database lookup failed - rc->value = NAN; + if(unlikely(!host->health_enabled || now < host->health_delay_up_to)) + continue; - debug(D_HEALTH, "Health alarm '%s.%s': database lookup returned error %d", rc->chart?rc->chart:"NOCHART", rc->name, ret); + rrdhost_rdlock(host); - 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); - } + // the first loop is to lookup values from the db + for(rc = host->alarms; rc; rc = rc->next) { + if(unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) { + if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE)) + rc->rrdcalc_flags &= ~RRDCALC_FLAG_RUNNABLE; + continue; } - else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_ERROR)) - rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_ERROR; - /* - RRDCALC_FLAG_DB_STALE not currently used - if (unlikely(old_db_timestamp == rc->db_before)) { - // database is stale + runnable++; + rc->old_value = rc->value; + rc->rrdcalc_flags |= RRDCALC_FLAG_RUNNABLE; + + // ------------------------------------------------------------ + // if there is database lookup, do it + + if(unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) { + /* time_t old_db_timestamp = rc->db_before; */ + int value_is_null = 0; + + int ret = rrdset2value_api_v1(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 + ); - debug(D_HEALTH, "Health alarm '%s.%s': database is stale", rc->chart?rc->chart:"NOCHART", rc->name); + if(unlikely(ret != 200)) { + // database lookup failed + rc->value = NAN; + rc->rrdcalc_flags |= RRDCALC_FLAG_DB_ERROR; - 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); + debug(D_HEALTH + , "Health on host '%s', alarm '%s.%s': database lookup returned error %d" + , host->hostname + , rc->chart ? rc->chart : "NOCHART" + , rc->name + , ret + ); } - } - else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE)) - rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_STALE; - */ + else + rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_ERROR; - if (unlikely(value_is_null)) { - // collected value is null + /* - RRDCALC_FLAG_DB_STALE not currently used + if (unlikely(old_db_timestamp == rc->db_before)) { + // database is stale - rc->value = NAN; + debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database is stale", host->hostname, rc->chart?rc->chart:"NOCHART", rc->name); - 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_STALE))) { + rc->rrdcalc_flags |= RRDCALC_FLAG_DB_STALE; + error("Health on host '%s', alarm '%s.%s': database is stale", host->hostname, 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(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_NAN))) { + if(unlikely(value_is_null)) { + // collected value is null + rc->value = 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); + + debug(D_HEALTH + , "Health on host '%s', alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)" + , host->hostname + , rc->chart ? rc->chart : "NOCHART" + , rc->name + ); } + else + rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_NAN; + + debug(D_HEALTH + , "Health on host '%s', alarm '%s.%s': database lookup gave value " CALCULATED_NUMBER_FORMAT + , host->hostname + , rc->chart ? rc->chart : "NOCHART" + , rc->name + , rc->value + ); } - 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 + // ------------------------------------------------------------ + // if there is calculation expression, run it - rc->value = NAN; - - debug(D_HEALTH, "Health alarm '%s.%s': expression '%s' failed: %s", - rc->chart?rc->chart:"NOCHART", rc->name, rc->calculation->parsed_as, buffer_tostring(rc->calculation->error_msg)); - - if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_CALC_ERROR))) { + if(unlikely(rc->calculation)) { + if(unlikely(!expression_evaluate(rc->calculation))) { + // calculation failed + rc->value = NAN; rc->rrdcalc_flags |= RRDCALC_FLAG_CALC_ERROR; - error("Health alarm '%s.%s': expression '%s' failed: %s", - rc->chart?rc->chart:"NOCHART", rc->name, rc->calculation->parsed_as, buffer_tostring(rc->calculation->error_msg)); + + debug(D_HEALTH + , "Health on host '%s', alarm '%s.%s': expression '%s' failed: %s" + , host->hostname + , rc->chart ? rc->chart : "NOCHART" + , rc->name + , rc->calculation->parsed_as + , buffer_tostring(rc->calculation->error_msg) + ); } - } - else { - if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_CALC_ERROR)) + else { rc->rrdcalc_flags &= ~RRDCALC_FLAG_CALC_ERROR; - debug(D_HEALTH, "Health alarm '%s.%s': expression '%s' gave value " - CALCULATED_NUMBER_FORMAT - ": %s (source: %s)", - rc->chart?rc->chart:"NOCHART", rc->name, - rc->calculation->parsed_as, - rc->calculation->result, - buffer_tostring(rc->calculation->error_msg), - rc->source - ); + debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' gave value " CALCULATED_NUMBER_FORMAT ": %s (source: %s)" + , host->hostname + , rc->chart ? rc->chart : "NOCHART" + , rc->name + , rc->calculation->parsed_as + , rc->calculation->result + , buffer_tostring(rc->calculation->error_msg) + , rc->source + ); - rc->value = rc->calculation->result; + rc->value = rc->calculation->result; + } } } - } - rrdhost_unlock(&localhost); + rrdhost_unlock(host); - if(unlikely(runnable && !netdata_exit)) { - rrdhost_rdlock(&localhost); + if(unlikely(runnable && !netdata_exit)) { + rrdhost_rdlock(host); - for(rc = localhost.alarms; rc; rc = rc->next) { - if(unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE))) - continue; + for(rc = host->alarms; rc; rc = rc->next) { + if(unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE))) + continue; - int warning_status = RRDCALC_STATUS_UNDEFINED; - int critical_status = RRDCALC_STATUS_UNDEFINED; + int warning_status = RRDCALC_STATUS_UNDEFINED; + int critical_status = RRDCALC_STATUS_UNDEFINED; - if(likely(rc->warning)) { - if(unlikely(!expression_evaluate(rc->warning))) { - // calculation failed + // -------------------------------------------------------- + // check the warning expression - 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))) { + if(likely(rc->warning)) { + if(unlikely(!expression_evaluate(rc->warning))) { + // calculation failed 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)); + + debug(D_HEALTH + , "Health on host '%s', alarm '%s.%s': warning expression failed with error: %s" + , host->hostname + , rc->chart ? rc->chart : "NOCHART" + , rc->name + , buffer_tostring(rc->warning->error_msg) + ); } - } - else { - if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_WARN_ERROR)) + else { 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); + debug(D_HEALTH + , "Health on host '%s', alarm '%s.%s': warning expression gave value " CALCULATED_NUMBER_FORMAT ": %s (source: %s)" + , host->hostname + , 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(likely(rc->critical)) { - if(unlikely(!expression_evaluate(rc->critical))) { - // calculation failed + // -------------------------------------------------------- + // check the critical expression - 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))) { + if(likely(rc->critical)) { + if(unlikely(!expression_evaluate(rc->critical))) { + // calculation failed 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)); + + debug(D_HEALTH + , "Health on host '%s', alarm '%s.%s': critical expression failed with error: %s" + , host->hostname + , rc->chart ? rc->chart : "NOCHART" + , rc->name + , buffer_tostring(rc->critical->error_msg) + ); } - } - else { - if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_CRIT_ERROR)) + else { rc->rrdcalc_flags &= ~RRDCALC_FLAG_CRIT_ERROR; + debug(D_HEALTH + , "Health on host '%s', alarm '%s.%s': critical expression gave value " CALCULATED_NUMBER_FORMAT ": %s (source: %s)" + , host->hostname + , 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); + } + } - 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 - ); + // -------------------------------------------------------- + // decide the final alarm status - 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; } - } - int status = RRDCALC_STATUS_UNDEFINED; + switch(critical_status) { + case RRDCALC_STATUS_CLEAR: + if(status == RRDCALC_STATUS_UNDEFINED) + status = RRDCALC_STATUS_CLEAR; + break; - switch(warning_status) { - case RRDCALC_STATUS_CLEAR: - status = RRDCALC_STATUS_CLEAR; - break; + case RRDCALC_STATUS_RAISED: + status = RRDCALC_STATUS_CRITICAL; + break; - case RRDCALC_STATUS_RAISED: - status = RRDCALC_STATUS_WARNING; - break; + default: + break; + } - default: - break; - } + // -------------------------------------------------------- + // check if the new status and the old differ - switch(critical_status) { - case RRDCALC_STATUS_CLEAR: - if(status == RRDCALC_STATUS_UNDEFINED) - status = RRDCALC_STATUS_CLEAR; - break; + if(status != rc->status) { + int delay = 0; - case RRDCALC_STATUS_RAISED: - status = RRDCALC_STATUS_CRITICAL; - break; + // apply trigger hysteresis - default: - break; - } + if(now > rc->delay_up_to_timestamp) { + rc->delay_up_current = rc->delay_up_duration; + rc->delay_down_current = rc->delay_down_duration; + rc->delay_last = 0; + rc->delay_up_to_timestamp = 0; + } + else { + rc->delay_up_current = (int) (rc->delay_up_current * rc->delay_multiplier); + if(rc->delay_up_current > rc->delay_max_duration) + rc->delay_up_current = rc->delay_max_duration; + + rc->delay_down_current = (int) (rc->delay_down_current * rc->delay_multiplier); + if(rc->delay_down_current > rc->delay_max_duration) + rc->delay_down_current = rc->delay_max_duration; + } - if(status != rc->status) { - int delay = 0; + if(status > rc->status) + delay = rc->delay_up_current; + else + delay = rc->delay_down_current; + + // COMMENTED: because we do need to send raising alarms + // if(now + delay < rc->delay_up_to_timestamp) + // delay = (int)(rc->delay_up_to_timestamp - now); + + rc->delay_last = delay; + rc->delay_up_to_timestamp = now + delay; + + // add the alarm into the log + + health_alarm_log( + host + , rc->id + , rc->next_event_id++ + , now + , rc->name + , rc->rrdset->id + , rc->rrdset->family + , rc->exec + , rc->recipient + , now - rc->last_status_change + , rc->old_value + , rc->value + , rc->status + , status + , rc->source + , rc->units + , rc->info + , rc->delay_last + , (rc->options & RRDCALC_FLAG_NO_CLEAR_NOTIFICATION) ? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0 + ); - if(now > rc->delay_up_to_timestamp) { - rc->delay_up_current = rc->delay_up_duration; - rc->delay_down_current = rc->delay_down_duration; - rc->delay_last = 0; - rc->delay_up_to_timestamp = 0; + rc->last_status_change = now; + rc->status = status; } - else { - rc->delay_up_current = (int)(rc->delay_up_current * rc->delay_multiplier); - if(rc->delay_up_current > rc->delay_max_duration) rc->delay_up_current = rc->delay_max_duration; - rc->delay_down_current = (int)(rc->delay_down_current * rc->delay_multiplier); - if(rc->delay_down_current > rc->delay_max_duration) rc->delay_down_current = rc->delay_max_duration; - } + rc->last_updated = now; + rc->next_update = now + rc->update_every; - if(status > rc->status) - delay = rc->delay_up_current; - else - delay = rc->delay_down_current; + if(next_run > rc->next_update) + next_run = rc->next_update; + } - // COMMENTED: because we do need to send raising alarms - // if(now + delay < rc->delay_up_to_timestamp) - // delay = (int)(rc->delay_up_to_timestamp - now); + rrdhost_unlock(host); + } - rc->delay_last = delay; - rc->delay_up_to_timestamp = now + delay; - health_alarm_log(&localhost, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, rc->recipient, now - rc->last_status_change, rc->old_value, rc->value, rc->status, status, rc->source, rc->units, rc->info, rc->delay_last); - rc->last_status_change = now; - rc->status = status; - } + if(unlikely(netdata_exit)) + break; - rc->last_updated = now; - rc->next_update = now + rc->update_every; + // execute notifications + // and cleanup + health_alarm_log_process(host); - if (next_run > rc->next_update) - next_run = rc->next_update; - } + if(unlikely(netdata_exit)) + break; - rrdhost_unlock(&localhost); - } + } /* rrdhost_foreach */ + + rrd_unlock(); - if (unlikely(pthread_setcancelstate(oldstate, NULL) != 0)) + if(unlikely(pthread_setcancelstate(oldstate, NULL) != 0)) error("Cannot set pthread cancel state to RESTORE (%d).", oldstate); if(unlikely(netdata_exit)) break; - // execute notifications - // and cleanup - health_alarm_log_process(&localhost); - - if(unlikely(netdata_exit)) - break; - now = now_realtime_sec(); if(now < next_run) { - debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs", - loop, (int) (next_run - now)); + debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs", loop, (int) (next_run - now)); sleep_usec(USEC_PER_SEC * (usec_t) (next_run - now)); + now = now_realtime_sec(); } - else { + else debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration now", loop); - } - } + + now_boottime = now_boottime_sec(); + + } // forever buffer_free(wb); diff --git a/src/health.h b/src/health.h index 79831d4fc..7028a914b 100644 --- a/src/health.h +++ b/src/health.h @@ -1,7 +1,7 @@ #ifndef NETDATA_HEALTH_H #define NETDATA_HEALTH_H -extern int health_enabled; +extern int default_health_enabled; extern int rrdvar_compare(void *a, void *b); @@ -119,13 +119,14 @@ typedef struct rrddimvar { #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 -#define RRDCALC_FLAG_RUNNABLE 0x00000040 +#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 +#define RRDCALC_FLAG_RUNNABLE 0x00000040 +#define RRDCALC_FLAG_NO_CLEAR_NOTIFICATION 0x80000000 typedef struct rrdcalc { uint32_t id; // the unique id of this alarm @@ -274,11 +275,12 @@ typedef struct rrdcalctemplate { #define RRDCALCTEMPLATE_HAS_CALCULATION(rt) ((rt)->after) -#define HEALTH_ENTRY_FLAG_PROCESSED 0x00000001 -#define HEALTH_ENTRY_FLAG_UPDATED 0x00000002 -#define HEALTH_ENTRY_FLAG_EXEC_RUN 0x00000004 -#define HEALTH_ENTRY_FLAG_EXEC_FAILED 0x00000008 -#define HEALTH_ENTRY_FLAG_SAVED 0x10000000 +#define HEALTH_ENTRY_FLAG_PROCESSED 0x00000001 +#define HEALTH_ENTRY_FLAG_UPDATED 0x00000002 +#define HEALTH_ENTRY_FLAG_EXEC_RUN 0x00000004 +#define HEALTH_ENTRY_FLAG_EXEC_FAILED 0x00000008 +#define HEALTH_ENTRY_FLAG_SAVED 0x10000000 +#define HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION 0x80000000 typedef struct alarm_entry { uint32_t unique_id; @@ -308,6 +310,10 @@ typedef struct alarm_entry { calculated_number old_value; calculated_number new_value; + + char *old_value_string; + char *new_value_string; + int old_status; int new_status; @@ -328,7 +334,7 @@ typedef struct alarm_log { unsigned int count; unsigned int max; ALARM_ENTRY *alarms; - pthread_rwlock_t alarm_log_rwlock; + netdata_rwlock_t alarm_log_rwlock; } ALARM_LOG; #include "rrd.h" @@ -363,4 +369,58 @@ extern void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value) extern const char *rrdcalc_status2string(int status); + +extern int health_alarm_log_open(RRDHOST *host); +extern void health_alarm_log_close(RRDHOST *host); +extern void health_log_rotate(RRDHOST *host); +extern void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae); +extern ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *filename); +extern void health_alarm_log_load(RRDHOST *host); +extern void health_alarm_log( + RRDHOST *host, + uint32_t alarm_id, + uint32_t alarm_event_id, + time_t when, + const char *name, + const char *chart, + const char *family, + const char *exec, + const char *recipient, + 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, + int delay, + uint32_t flags +); + +extern void health_readdir(RRDHOST *host, const char *path); +extern char *health_config_dir(void); +extern void health_reload_host(RRDHOST *host); +extern void health_alarm_log_free(RRDHOST *host); + +extern void rrdcalc_free(RRDHOST *host, RRDCALC *rc); +extern void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt); + +#ifdef NETDATA_HEALTH_INTERNALS +#define RRDVAR_MAX_LENGTH 1024 + +extern int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name); +extern uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id); +extern int rrdvar_fix_name(char *variable); + +extern RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart); +extern void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc); + +extern RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *value); +extern void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv); + +extern void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae); + +#endif // NETDATA_HEALTH_INTERNALS + #endif //NETDATA_HEALTH_H diff --git a/src/health_config.c b/src/health_config.c new file mode 100644 index 000000000..ad954cbe1 --- /dev/null +++ b/src/health_config.c @@ -0,0 +1,877 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +#define HEALTH_CONF_MAX_LINE 4096 + +#define HEALTH_ALARM_KEY "alarm" +#define HEALTH_TEMPLATE_KEY "template" +#define HEALTH_ON_KEY "on" +#define HEALTH_FAMILIES_KEY "families" +#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_RECIPIENT_KEY "to" +#define HEALTH_UNITS_KEY "units" +#define HEALTH_INFO_KEY "info" +#define HEALTH_DELAY_KEY "delay" +#define HEALTH_OPTIONS_KEY "options" + +static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) { + 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; + } + + if (rrdcalc_exists(host, rc->chart, rc->name, rc->hash_chart, rc->hash)) + return 0; + + rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->name, &rc->next_event_id); + + debug(D_HEALTH, "Health configuration adding alarm '%s.%s' (%u): exec '%s', recipient '%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', delay up %d, delay down %d, delay max %d, delay_multiplier %f", + rc->chart?rc->chart:"NOCHART", + rc->name, + rc->id, + (rc->exec)?rc->exec:"DEFAULT", + (rc->recipient)?rc->recipient:"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, + rc->delay_up_duration, + rc->delay_down_duration, + rc->delay_max_duration, + rc->delay_multiplier + ); + + rrdcalc_create_part2(host, rc); + return 1; +} + +static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) { + if(unlikely(!rt->context)) { + error("Health configuration for template '%s' does not have a context", rt->name); + return 0; + } + + if(unlikely(!rt->update_every)) { + error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rt->name); + return 0; + } + + if(unlikely(!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, *last = NULL; + for (t = host->templates; t ; last = t, t = t->next) { + if(unlikely(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', recipient '%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', delay up %d, delay down %d, delay max %d, delay_multiplier %f", + rt->name, + (rt->context)?rt->context:"NONE", + (rt->exec)?rt->exec:"DEFAULT", + (rt->recipient)?rt->recipient:"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->delay_up_duration, + rt->delay_down_duration, + rt->delay_max_duration, + rt->delay_multiplier + ); + + if(likely(last)) { + last->next = rt; + } + else { + 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_delay( + size_t line, const char *path, const char *file, char *string, + int *delay_up_duration, + int *delay_down_duration, + int *delay_max_duration, + float *delay_multiplier) { + + char given_up = 0; + char given_down = 0; + char given_max = 0; + char given_multiplier = 0; + + char *s = string; + while(*s) { + char *key = s; + + while(*s && !isspace(*s)) s++; + while(*s && isspace(*s)) *s++ = '\0'; + + if(!*key) break; + + char *value = s; + while(*s && !isspace(*s)) s++; + while(*s && isspace(*s)) *s++ = '\0'; + + if(!strcasecmp(key, "up")) { + if (!health_parse_duration(value, delay_up_duration)) { + error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword", + line, path, file, value, key); + } + else given_up = 1; + } + else if(!strcasecmp(key, "down")) { + if (!health_parse_duration(value, delay_down_duration)) { + error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword", + line, path, file, value, key); + } + else given_down = 1; + } + else if(!strcasecmp(key, "multiplier")) { + *delay_multiplier = strtof(value, NULL); + if(isnan(*delay_multiplier) || isinf(*delay_multiplier) || islessequal(*delay_multiplier, 0)) { + error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword", + line, path, file, value, key); + } + else given_multiplier = 1; + } + else if(!strcasecmp(key, "max")) { + if (!health_parse_duration(value, delay_max_duration)) { + error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword", + line, path, file, value, key); + } + else given_max = 1; + } + else { + error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'", + line, path, file, key); + } + } + + if(!given_up) + *delay_up_duration = 0; + + if(!given_down) + *delay_down_duration = 0; + + if(!given_multiplier) + *delay_multiplier = 1.0; + + if(!given_max) { + if((*delay_max_duration) < (*delay_up_duration) * (*delay_multiplier)) + *delay_max_duration = (*delay_up_duration) * (*delay_multiplier); + + if((*delay_max_duration) < (*delay_down_duration) * (*delay_multiplier)) + *delay_max_duration = (*delay_down_duration) * (*delay_multiplier); + } + + return 1; +} + +static inline uint32_t health_parse_options(const char *s) { + uint32_t options = 0; + char buf[100+1] = ""; + + while(*s) { + buf[0] = '\0'; + + // skip spaces + while(*s && isspace(*s)) + s++; + + // find the next space + size_t count = 0; + while(*s && count < 100 && !isspace(*s)) + buf[count++] = *s++; + + if(buf[0]) { + buf[count] = '\0'; + + if(!strcasecmp(buf, "no-clear-notification") || !strcasecmp(buf, "no-clear")) + options |= RRDCALC_FLAG_NO_CLEAR_NOTIFICATION; + else + error("Ignoring unknown alarm option '%s'", buf); + } + } + + return options; +} + +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 *trim_all_spaces(char *buffer) { + char *d = buffer, *s = buffer; + + // skip spaces + while(isspace(*s)) s++; + + while(*s) { + // copy the non-space part + while(*s && !isspace(*s)) *d++ = *s++; + + // add a space if we have to + if(*s && isspace(*s)) { + *d++ = ' '; + s++; + } + + // skip spaces + while(isspace(*s)) s++; + } + + *d = '\0'; + + if(d > buffer) { + d--; + if(isspace(*d)) *d = '\0'; + } + + if(!buffer[0]) return NULL; + return buffer; +} + +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(RRDHOST *host, 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_families = 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, + hash_recipient = 0, + hash_delay = 0, + hash_options = 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_families = simple_uhash(HEALTH_FAMILIES_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); + hash_recipient = simple_hash(HEALTH_RECIPIENT_KEY); + hash_delay = simple_uhash(HEALTH_DELAY_KEY); + hash_options = simple_uhash(HEALTH_OPTIONS_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++; + s = trim(buffer); + if(!s) continue; + + 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_all_spaces(key); + value = trim_all_spaces(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; + } + + uint32_t hash = simple_uhash(key); + + if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) { + if(rc && !rrdcalc_add_alarm_from_config(host, rc)) + rrdcalc_free(host, rc); + + if(rt) { + if (!rrdcalctemplate_add_template_from_config(host, rt)) + rrdcalctemplate_free(host, rt); + rt = NULL; + } + + rc = callocz(1, sizeof(RRDCALC)); + rc->next_event_id = 1; + 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; + rc->delay_multiplier = 1.0; + + 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(host, rc)) + rrdcalc_free(host, rc); + rc = NULL; + } + + if(rt && !rrdcalctemplate_add_template_from_config(host, rt)) + rrdcalctemplate_free(host, 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; + rt->delay_multiplier = 1.0; + + 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)) + error("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)) + error("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) { + error("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) { + error("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)) + error("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_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) { + if(rc->recipient) { + if(strcmp(rc->recipient, value)) + error("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->recipient, value, value); + + freez(rc->recipient); + } + rc->recipient = strdupz(value); + } + else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) { + if(rc->units) { + if(strcmp(rc->units, value)) + error("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)) + error("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 if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) { + health_parse_delay(line, path, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier); + } + else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) { + rc->options |= health_parse_options(value); + } + 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)) + error("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_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) { + freez(rt->family_match); + simple_pattern_free(rt->family_pattern); + + rt->family_match = strdupz(value); + rt->family_pattern = simple_pattern_create(rt->family_match, SIMPLE_PATTERN_EXACT); + } + 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)) + error("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) { + error("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) { + error("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)) + error("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_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) { + if(rt->recipient) { + if(strcmp(rt->recipient, value)) + error("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->recipient, value, value); + + freez(rt->recipient); + } + rt->recipient = strdupz(value); + } + else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) { + if(rt->units) { + if(strcmp(rt->units, value)) + error("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)) + error("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 if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) { + health_parse_delay(line, path, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier); + } + else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) { + rt->options |= health_parse_options(value); + } + 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(host, rc)) + rrdcalc_free(host, rc); + + if(rt && !rrdcalctemplate_add_template_from_config(host, rt)) + rrdcalctemplate_free(host, rt); + + fclose(fp); + return 1; +} + +void health_readdir(RRDHOST *host, const char *path) { + if(!host->health_enabled) return; + + 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') + )) { + debug(D_HEALTH, "Ignoring directory '%s'", de->d_name); + 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(host, s); + freez(s); + continue; + } + + else if((de->d_type == DT_LNK || de->d_type == DT_REG || de->d_type == DT_UNKNOWN) && + len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) { + health_readfile(host, path, de->d_name); + } + + else debug(D_HEALTH, "Ignoring file '%s'", de->d_name); + } + + closedir(dir); +} + + diff --git a/src/health_json.c b/src/health_json.c new file mode 100644 index 000000000..a9697aaa7 --- /dev/null +++ b/src/health_json.c @@ -0,0 +1,256 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +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, RRDHOST *host) { + buffer_sprintf(wb, + "\n\t{\n" + "\t\t\"hostname\": \"%s\",\n" + "\t\t\"unique_id\": %u,\n" + "\t\t\"alarm_id\": %u,\n" + "\t\t\"alarm_event_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\": %lu,\n" + "\t\t\"exec_failed\": %s,\n" + "\t\t\"exec\": \"%s\",\n" + "\t\t\"recipient\": \"%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" + "\t\t\"delay\": %d,\n" + "\t\t\"delay_up_to_timestamp\": %lu,\n" + "\t\t\"updated_by_id\": %u,\n" + "\t\t\"updates_id\": %u,\n" + "\t\t\"value_string\": \"%s\",\n" + "\t\t\"old_value_string\": \"%s\",\n" + , host->hostname + , ae->unique_id + , ae->alarm_id + , ae->alarm_event_id + , ae->name + , ae->chart + , ae->family + , (ae->flags & HEALTH_ENTRY_FLAG_PROCESSED)?"true":"false" + , (ae->flags & HEALTH_ENTRY_FLAG_UPDATED)?"true":"false" + , (unsigned long)ae->exec_run_timestamp + , (ae->flags & HEALTH_ENTRY_FLAG_EXEC_FAILED)?"true":"false" + , ae->exec?ae->exec:host->health_default_exec + , ae->recipient?ae->recipient:host->health_default_recipient + , 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) + , ae->delay + , (unsigned long)ae->delay_up_to_timestamp + , ae->updated_by_id + , ae->updates_id + , ae->new_value_string + , ae->old_value_string + ); + + if(unlikely(ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION)) { + buffer_strcat(wb, "\t\t\"no_clear_notification\": true,\n"); + } + + 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, uint32_t after) { + netdata_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(ae->unique_id > after) { + if(likely(count)) buffer_strcat(wb, ","); + health_alarm_entry2json_nolock(wb, ae, host); + } + } + + buffer_strcat(wb, "\n]\n"); + + netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); +} + +static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC *rc) { + char value_string[100 + 1]; + format_value_and_unit(value_string, 100, rc->value, rc->units, -1); + + buffer_sprintf(wb, + "\t\t\"%s.%s\": {\n" + "\t\t\t\"id\": %lu,\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\"recipient\": \"%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" + "\t\t\t\"delay_up_duration\": %d,\n" + "\t\t\t\"delay_down_duration\": %d,\n" + "\t\t\t\"delay_max_duration\": %d,\n" + "\t\t\t\"delay_multiplier\": %f,\n" + "\t\t\t\"delay\": %d,\n" + "\t\t\t\"delay_up_to_timestamp\": %lu,\n" + "\t\t\t\"value_string\": \"%s\",\n" + , rc->chart, rc->name + , (unsigned long)rc->id + , rc->name + , rc->chart + , (rc->rrdset && rc->rrdset->family)?rc->rrdset->family:"" + , (rc->rrdset)?"true":"false" + , rc->exec?rc->exec:host->health_default_exec + , rc->recipient?rc->recipient:host->health_default_recipient + , 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 + , rc->delay_up_duration + , rc->delay_down_duration + , rc->delay_max_duration + , rc->delay_multiplier + , rc->delay_last + , (unsigned long)rc->delay_up_to_timestamp + , value_string + ); + + if(unlikely(rc->options & RRDCALC_FLAG_NO_CLEAR_NOTIFICATION)) { + buffer_strcat(wb, "\t\t\t\"no_clear_notification\": true,\n"); + } + + 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(host); + buffer_sprintf(wb, "{\n\t\"hostname\": \"%s\"," + "\n\t\"latest_alarm_log_unique_id\": %u," + "\n\t\"status\": %s," + "\n\t\"now\": %lu," + "\n\t\"alarms\": {\n", + host->hostname, + (host->health_log.next_log_id > 0)?(host->health_log.next_log_id - 1):0, + host->health_enabled?"true":"false", + (unsigned long)now_realtime_sec()); + + RRDCALC *rc; + for(i = 0, rc = host->alarms; rc ; rc = rc->next) { + if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) + continue; + + if(likely(!all && !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL))) + continue; + + if(likely(i)) buffer_strcat(wb, ",\n"); + health_rrdcalc2json_nolock(host, 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_strcat(wb, "\n\t}\n}\n"); + rrdhost_unlock(host); +} + + + diff --git a/src/health_log.c b/src/health_log.c new file mode 100644 index 000000000..95abcfe5f --- /dev/null +++ b/src/health_log.c @@ -0,0 +1,465 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// health alarm log load/save +// no need for locking - only one thread is reading / writing the alarms log + +inline int health_alarm_log_open(RRDHOST *host) { + if(host->health_log_fp) + fclose(host->health_log_fp); + + host->health_log_fp = fopen(host->health_log_filename, "a"); + + if(host->health_log_fp) { + if (setvbuf(host->health_log_fp, NULL, _IOLBF, 0) != 0) + error("HEALTH [%s]: cannot set line buffering on health log file '%s'.", host->hostname, host->health_log_filename); + return 0; + } + + error("HEALTH [%s]: cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", host->hostname, host->health_log_filename); + return -1; +} + +inline void health_alarm_log_close(RRDHOST *host) { + if(host->health_log_fp) { + fclose(host->health_log_fp); + host->health_log_fp = NULL; + } +} + +inline void health_log_rotate(RRDHOST *host) { + static size_t rotate_every = 0; + + if(unlikely(rotate_every == 0)) { + rotate_every = (size_t)config_get_number(CONFIG_SECTION_HEALTH, "rotate log every lines", 2000); + if(rotate_every < 100) rotate_every = 100; + } + + if(unlikely(host->health_log_entries_written > rotate_every)) { + health_alarm_log_close(host); + + char old_filename[FILENAME_MAX + 1]; + snprintfz(old_filename, FILENAME_MAX, "%s.old", host->health_log_filename); + + if(unlink(old_filename) == -1 && errno != ENOENT) + error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, old_filename); + + if(link(host->health_log_filename, old_filename) == -1 && errno != ENOENT) + error("HEALTH [%s]: cannot move file '%s' to '%s'.", host->hostname, host->health_log_filename, old_filename); + + if(unlink(host->health_log_filename) == -1 && errno != ENOENT) + error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, host->health_log_filename); + + // open it with truncate + host->health_log_fp = fopen(host->health_log_filename, "w"); + + if(host->health_log_fp) + fclose(host->health_log_fp); + else + error("HEALTH [%s]: cannot truncate health log '%s'", host->hostname, host->health_log_filename); + + host->health_log_fp = NULL; + + host->health_log_entries_written = 0; + health_alarm_log_open(host); + } +} + +inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) { + health_log_rotate(host); + + if(likely(host->health_log_fp)) { + if(unlikely(fprintf(host->health_log_fp + , "%c\t%s" + "\t%08x\t%08x\t%08x\t%08x\t%08x" + "\t%08x\t%08x\t%08x" + "\t%08x\t%08x\t%08x" + "\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" + "\t%d\t%d\t%d\t%d" + "\t%Lf\t%Lf" + "\n" + , (ae->flags & HEALTH_ENTRY_FLAG_SAVED)?'U':'A' + , host->hostname + + , ae->unique_id + , ae->alarm_id + , ae->alarm_event_id + , ae->updated_by_id + , ae->updates_id + + , (uint32_t)ae->when + , (uint32_t)ae->duration + , (uint32_t)ae->non_clear_duration + , (uint32_t)ae->flags + , (uint32_t)ae->exec_run_timestamp + , (uint32_t)ae->delay_up_to_timestamp + + , (ae->name)?ae->name:"" + , (ae->chart)?ae->chart:"" + , (ae->family)?ae->family:"" + , (ae->exec)?ae->exec:"" + , (ae->recipient)?ae->recipient:"" + , (ae->source)?ae->source:"" + , (ae->units)?ae->units:"" + , (ae->info)?ae->info:"" + + , ae->exec_code + , ae->new_status + , ae->old_status + , ae->delay + + , (long double)ae->new_value + , (long double)ae->old_value + ) < 0)) + error("HEALTH [%s]: failed to save alarm log entry to '%s'. Health data may be lost in case of abnormal restart.", host->hostname, host->health_log_filename); + else { + ae->flags |= HEALTH_ENTRY_FLAG_SAVED; + host->health_log_entries_written++; + } + } +} + +inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *filename) { + static uint32_t max_unique_id = 0, max_alarm_id = 0; + + errno = 0; + + char *s, *buf = mallocz(65536 + 1); + size_t line = 0, len = 0; + ssize_t loaded = 0, updated = 0, errored = 0, duplicate = 0; + + netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock); + + while((s = fgets_trim_len(buf, 65536, fp, &len))) { + host->health_log_entries_written++; + line++; + + int max_entries = 30, entries = 0; + char *pointers[max_entries]; + + pointers[entries++] = s++; + while(*s) { + if(unlikely(*s == '\t')) { + *s = '\0'; + pointers[entries++] = ++s; + if(entries >= max_entries) { + error("HEALTH [%s]: line %zu of file '%s' has more than %d entries. Ignoring excessive entries.", host->hostname, line, filename, max_entries); + break; + } + } + else s++; + } + + if(likely(*pointers[0] == 'U' || *pointers[0] == 'A')) { + ALARM_ENTRY *ae = NULL; + + if(entries < 26) { + error("HEALTH [%s]: line %zu of file '%s' should have at least 26 entries, but it has %d. Ignoring it.", host->hostname, line, filename, entries); + errored++; + continue; + } + + // check that we have valid ids + uint32_t unique_id = (uint32_t)strtoul(pointers[2], NULL, 16); + if(!unique_id) { + error("HEALTH [%s]: line %zu of file '%s' states alarm entry with invalid unique id %u (%s). Ignoring it.", host->hostname, line, filename, unique_id, pointers[2]); + errored++; + continue; + } + + uint32_t alarm_id = (uint32_t)strtoul(pointers[3], NULL, 16); + if(!alarm_id) { + error("HEALTH [%s]: line %zu of file '%s' states alarm entry for invalid alarm id %u (%s). Ignoring it.", host->hostname, line, filename, alarm_id, pointers[3]); + errored++; + continue; + } + + if(unlikely(*pointers[0] == 'A')) { + // make sure it is properly numbered + if(unlikely(host->health_log.alarms && unique_id < host->health_log.alarms->unique_id)) { + error("HEALTH [%s]: line %zu of file '%s' has alarm log entry %u in wrong order. Ignoring it.", host->hostname, line, filename, unique_id); + errored++; + continue; + } + + ae = callocz(1, sizeof(ALARM_ENTRY)); + } + else if(unlikely(*pointers[0] == 'U')) { + // find the original + for(ae = host->health_log.alarms; ae; ae = ae->next) { + if(unlikely(unique_id == ae->unique_id)) { + if(unlikely(*pointers[0] == 'A')) { + error("HEALTH [%s]: line %zu of file '%s' adds duplicate alarm log entry %u. Using the later." + , host->hostname, line, filename, unique_id); + *pointers[0] = 'U'; + duplicate++; + } + break; + } + else if(unlikely(unique_id > ae->unique_id)) { + // no need to continue + // the linked list is sorted + ae = NULL; + break; + } + } + } + + // if not found, skip this line + if(unlikely(!ae)) { + // error("HEALTH [%s]: line %zu of file '%s' updates alarm log entry with unique id %u, but it is not found.", host->hostname, line, filename, unique_id); + continue; + } + + // check for a possible host missmatch + //if(strcmp(pointers[1], host->hostname)) + // error("HEALTH [%s]: line %zu of file '%s' provides an alarm for host '%s' but this is named '%s'.", host->hostname, line, filename, pointers[1], host->hostname); + + ae->unique_id = unique_id; + ae->alarm_id = alarm_id; + ae->alarm_event_id = (uint32_t)strtoul(pointers[4], NULL, 16); + ae->updated_by_id = (uint32_t)strtoul(pointers[5], NULL, 16); + ae->updates_id = (uint32_t)strtoul(pointers[6], NULL, 16); + + ae->when = (uint32_t)strtoul(pointers[7], NULL, 16); + ae->duration = (uint32_t)strtoul(pointers[8], NULL, 16); + ae->non_clear_duration = (uint32_t)strtoul(pointers[9], NULL, 16); + + ae->flags = (uint32_t)strtoul(pointers[10], NULL, 16); + ae->flags |= HEALTH_ENTRY_FLAG_SAVED; + + ae->exec_run_timestamp = (uint32_t)strtoul(pointers[11], NULL, 16); + ae->delay_up_to_timestamp = (uint32_t)strtoul(pointers[12], NULL, 16); + + freez(ae->name); + ae->name = strdupz(pointers[13]); + ae->hash_name = simple_hash(ae->name); + + freez(ae->chart); + ae->chart = strdupz(pointers[14]); + ae->hash_chart = simple_hash(ae->chart); + + freez(ae->family); + ae->family = strdupz(pointers[15]); + + freez(ae->exec); + ae->exec = strdupz(pointers[16]); + if(!*ae->exec) { freez(ae->exec); ae->exec = NULL; } + + freez(ae->recipient); + ae->recipient = strdupz(pointers[17]); + if(!*ae->recipient) { freez(ae->recipient); ae->recipient = NULL; } + + freez(ae->source); + ae->source = strdupz(pointers[18]); + if(!*ae->source) { freez(ae->source); ae->source = NULL; } + + freez(ae->units); + ae->units = strdupz(pointers[19]); + if(!*ae->units) { freez(ae->units); ae->units = NULL; } + + freez(ae->info); + ae->info = strdupz(pointers[20]); + if(!*ae->info) { freez(ae->info); ae->info = NULL; } + + ae->exec_code = str2i(pointers[21]); + ae->new_status = str2i(pointers[22]); + ae->old_status = str2i(pointers[23]); + ae->delay = str2i(pointers[24]); + + ae->new_value = str2l(pointers[25]); + ae->old_value = str2l(pointers[26]); + + static char value_string[100 + 1]; + freez(ae->old_value_string); + freez(ae->new_value_string); + ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1)); + ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1)); + + // add it to host if not already there + if(unlikely(*pointers[0] == 'A')) { + ae->next = host->health_log.alarms; + host->health_log.alarms = ae; + loaded++; + } + else updated++; + + if(unlikely(ae->unique_id > max_unique_id)) + max_unique_id = ae->unique_id; + + if(unlikely(ae->alarm_id >= max_alarm_id)) + max_alarm_id = ae->alarm_id; + } + else { + error("HEALTH [%s]: line %zu of file '%s' is invalid (unrecognized entry type '%s').", host->hostname, line, filename, pointers[0]); + errored++; + } + } + + netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); + + freez(buf); + + if(!max_unique_id) max_unique_id = (uint32_t)now_realtime_sec(); + if(!max_alarm_id) max_alarm_id = (uint32_t)now_realtime_sec(); + + host->health_log.next_log_id = max_unique_id + 1; + host->health_log.next_alarm_id = max_alarm_id + 1; + + debug(D_HEALTH, "HEALTH [%s]: loaded file '%s' with %zd new alarm entries, updated %zd alarms, errors %zd entries, duplicate %zd", host->hostname, filename, loaded, updated, errored, duplicate); + return loaded; +} + +inline void health_alarm_log_load(RRDHOST *host) { + health_alarm_log_close(host); + + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s.old", host->health_log_filename); + FILE *fp = fopen(filename, "r"); + if(!fp) + error("HEALTH [%s]: cannot open health file: %s", host->hostname, filename); + else { + health_alarm_log_read(host, fp, filename); + fclose(fp); + } + + host->health_log_entries_written = 0; + fp = fopen(host->health_log_filename, "r"); + if(!fp) + error("HEALTH [%s]: cannot open health file: %s", host->hostname, host->health_log_filename); + else { + health_alarm_log_read(host, fp, host->health_log_filename); + fclose(fp); + } + + health_alarm_log_open(host); +} + + +// ---------------------------------------------------------------------------- +// health alarm log management + +inline void health_alarm_log( + RRDHOST *host, + uint32_t alarm_id, + uint32_t alarm_event_id, + time_t when, + const char *name, + const char *chart, + const char *family, + const char *exec, + const char *recipient, + 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, + int delay, + uint32_t flags +) { + debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id); + + 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(recipient) ae->recipient = strdupz(recipient); + if(source) ae->source = strdupz(source); + if(units) ae->units = strdupz(units); + if(info) ae->info = strdupz(info); + + ae->unique_id = host->health_log.next_log_id++; + ae->alarm_id = alarm_id; + ae->alarm_event_id = alarm_event_id; + ae->when = when; + ae->old_value = old_value; + ae->new_value = new_value; + + static char value_string[100 + 1]; + ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1)); + ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1)); + + ae->old_status = old_status; + ae->new_status = new_status; + ae->duration = duration; + ae->delay = delay; + ae->delay_up_to_timestamp = when + delay; + + ae->flags |= flags; + + if(ae->old_status == RRDCALC_STATUS_WARNING || ae->old_status == RRDCALC_STATUS_CRITICAL) + ae->non_clear_duration += ae->duration; + + // link it + netdata_rwlock_wrlock(&host->health_log.alarm_log_rwlock); + ae->next = host->health_log.alarms; + host->health_log.alarms = ae; + host->health_log.count++; + netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); + + // match previous alarms + netdata_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->alarm_id == ae->alarm_id) { + if(!(t->flags & HEALTH_ENTRY_FLAG_UPDATED) && !t->updated_by_id) { + t->flags |= HEALTH_ENTRY_FLAG_UPDATED; + t->updated_by_id = ae->unique_id; + ae->updates_id = t->unique_id; + + 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; + + health_alarm_log_save(host, t); + } + + // no need to continue + break; + } + } + netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); + + health_alarm_log_save(host, ae); +} + +inline void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae) { + freez(ae->name); + freez(ae->chart); + freez(ae->family); + freez(ae->exec); + freez(ae->recipient); + freez(ae->source); + freez(ae->units); + freez(ae->info); + freez(ae->old_value_string); + freez(ae->new_value_string); + freez(ae); +} + +inline void health_alarm_log_free(RRDHOST *host) { + rrdhost_check_wrlock(host); + + netdata_rwlock_wrlock(&host->health_log.alarm_log_rwlock); + + ALARM_ENTRY *ae; + while((ae = host->health_log.alarms)) { + host->health_log.alarms = ae->next; + health_alarm_log_free_one_nochecks_nounlink(ae); + } + + netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock); +} diff --git a/src/inlined.h b/src/inlined.h index e776f830e..0dc11c950 100644 --- a/src/inlined.h +++ b/src/inlined.h @@ -3,6 +3,19 @@ #include "common.h" +#ifdef KERNEL_32BIT +typedef uint32_t kernel_uint_t; +#define str2kernel_uint_t(string) str2uint32_t(string) +#define KERNEL_UINT_FORMAT "%u" +#else +typedef uint64_t kernel_uint_t; +#define str2kernel_uint_t(string) str2uint64_t(string) +#define KERNEL_UINT_FORMAT "%" PRIu64 +#endif + +#define str2pid_t(string) str2uint32_t(string) + + // for faster execution, allow the compiler to inline // these functions that are called thousands of times per second @@ -70,6 +83,26 @@ static inline long str2l(const char *s) { return n; } +static inline uint32_t str2uint32_t(const char *s) { + uint32_t n = 0; + char c; + for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + n *= 10; + n += c - '0'; + } + return n; +} + +static inline uint64_t str2uint64_t(const char *s) { + uint64_t n = 0; + char c; + for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + n *= 10; + n += c - '0'; + } + return n; +} + static inline unsigned long str2ul(const char *s) { unsigned long n = 0; char c; @@ -95,12 +128,24 @@ static inline unsigned long long str2ull(const char *s) { #undef strcmp #endif #define strcmp(a, b) strsame(a, b) +#endif // NETDATA_STRCMP_OVERRIDE + static inline int strsame(const char *a, const char *b) { if(unlikely(a == b)) return 0; while(*a && *a == *b) { a++; b++; } return *a - *b; } -#endif // NETDATA_STRSAME + +static inline char *strncpyz(char *dst, const char *src, size_t n) { + char *p = dst; + + while (*src && n--) + *dst++ = *src++; + + *dst = '\0'; + + return p; +} static inline int read_single_number_file(const char *filename, unsigned long long *result) { char buffer[30 + 1]; @@ -57,7 +57,7 @@ static inline int ipc_sem_get_limits(struct ipc_limits *lim) { static char filename[FILENAME_MAX + 1] = ""; if(unlikely(!filename[0])) - snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/sem", global_host_prefix); + snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/sem", netdata_configured_host_prefix); if(unlikely(!ff)) { ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT); @@ -184,23 +184,27 @@ int do_ipc(int update_every, usec_t dt) { return 1; } - arrays_max = rrdvar_custom_host_variable_create(&localhost, "ipc.semaphores.arrays.max"); - semaphores_max = rrdvar_custom_host_variable_create(&localhost, "ipc.semaphores.max"); + arrays_max = rrdvar_custom_host_variable_create(localhost, "ipc.semaphores.arrays.max"); + semaphores_max = rrdvar_custom_host_variable_create(localhost, "ipc.semaphores.max"); if(arrays_max) rrdvar_custom_host_variable_set(arrays_max, limits.semmni); if(semaphores_max) rrdvar_custom_host_variable_set(semaphores_max, limits.semmns); // create the charts - semaphores = rrdset_find("system.ipc_semaphores"); + semaphores = rrdset_find_localhost("system.ipc_semaphores"); if(!semaphores) { - semaphores = rrdset_create("system", "ipc_semaphores", NULL, "ipc semaphores", NULL, "IPC Semaphores", "semaphores", 1000, rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(semaphores, "semaphores", NULL, 1, 1, RRDDIM_ABSOLUTE); + semaphores = rrdset_create_localhost("system", "ipc_semaphores", NULL, "ipc semaphores", NULL + , "IPC Semaphores", "semaphores", 1000, localhost->rrd_update_every + , RRDSET_TYPE_AREA); + rrddim_add(semaphores, "semaphores", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - arrays = rrdset_find("system.ipc_semaphore_arrays"); + arrays = rrdset_find_localhost("system.ipc_semaphore_arrays"); if(!arrays) { - arrays = rrdset_create("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL, "IPC Semaphore Arrays", "arrays", 1000, rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(arrays, "arrays", NULL, 1, 1, RRDDIM_ABSOLUTE); + arrays = rrdset_create_localhost("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL + , "IPC Semaphore Arrays", "arrays", 1000, localhost->rrd_update_every + , RRDSET_TYPE_AREA); + rrddim_add(arrays, "arrays", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } } diff --git a/src/locks.h b/src/locks.h new file mode 100644 index 000000000..76533f636 --- /dev/null +++ b/src/locks.h @@ -0,0 +1,294 @@ +#ifndef NETDATA_LOCKS_H +#define NETDATA_LOCKS_H + +// ---------------------------------------------------------------------------- +// mutex + +typedef pthread_mutex_t netdata_mutex_t; + +#define NETDATA_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +static inline int __netdata_mutex_init(netdata_mutex_t *mutex) { + int ret = pthread_mutex_init(mutex, NULL); + if(unlikely(ret != 0)) + error("MUTEX_LOCK: failed to initialize (code %d).", ret); + return ret; +} + +static inline int __netdata_mutex_lock(netdata_mutex_t *mutex) { + int ret = pthread_mutex_lock(mutex); + if(unlikely(ret != 0)) + error("MUTEX_LOCK: failed to get lock (code %d)", ret); + return ret; +} + +static inline int __netdata_mutex_trylock(netdata_mutex_t *mutex) { + int ret = pthread_mutex_trylock(mutex); + return ret; +} + +static inline int __netdata_mutex_unlock(netdata_mutex_t *mutex) { + int ret = pthread_mutex_unlock(mutex); + if(unlikely(ret != 0)) + error("MUTEX_LOCK: failed to unlock (code %d).", ret); + return ret; +} + +#ifdef NETDATA_INTERNAL_CHECKS + +static inline int netdata_mutex_init_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_init(0x%p) from %lu@%s, %s()", mutex, line, file, function); + } + + int ret = __netdata_mutex_init(mutex); + + debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_init(0x%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +static inline int netdata_mutex_lock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_lock(0x%p) from %lu@%s, %s()", mutex, line, file, function); + } + + int ret = __netdata_mutex_lock(mutex); + + debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_lock(0x%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +static inline int netdata_mutex_trylock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_trylock(0x%p) from %lu@%s, %s()", mutex, line, file, function); + } + + int ret = __netdata_mutex_trylock(mutex); + + debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_trylock(0x%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +static inline int netdata_mutex_unlock_debug( const char *file, const char *function, const unsigned long line, netdata_mutex_t *mutex) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_unlock(0x%p) from %lu@%s, %s()", mutex, line, file, function); + } + + int ret = __netdata_mutex_unlock(mutex); + + debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_unlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +#define netdata_mutex_init(mutex) netdata_mutex_init_debug(__FILE__, __FUNCTION__, __LINE__, mutex) +#define netdata_mutex_lock(mutex) netdata_mutex_lock_debug(__FILE__, __FUNCTION__, __LINE__, mutex) +#define netdata_mutex_trylock(mutex) netdata_mutex_trylock_debug(__FILE__, __FUNCTION__, __LINE__, mutex) +#define netdata_mutex_unlock(mutex) netdata_mutex_unlock_debug(__FILE__, __FUNCTION__, __LINE__, mutex) + +#else // !NETDATA_INTERNAL_CHECKS + +#define netdata_mutex_init(mutex) __netdata_mutex_init(mutex) +#define netdata_mutex_lock(mutex) __netdata_mutex_lock(mutex) +#define netdata_mutex_trylock(mutex) __netdata_mutex_trylock(mutex) +#define netdata_mutex_unlock(mutex) __netdata_mutex_unlock(mutex) + +#endif // NETDATA_INTERNAL_CHECKS + + +// ---------------------------------------------------------------------------- +// r/w lock + +typedef pthread_rwlock_t netdata_rwlock_t; + +#define NETDATA_RWLOCK_INITIALIZER PTHREAD_RWLOCK_INITIALIZER + +static inline int __netdata_rwlock_destroy(netdata_rwlock_t *rwlock) { + int ret = pthread_rwlock_destroy(rwlock); + if(unlikely(ret != 0)) + error("RW_LOCK: failed to destroy lock (code %d)", ret); + return ret; +} + +static inline int __netdata_rwlock_init(netdata_rwlock_t *rwlock) { + int ret = pthread_rwlock_init(rwlock, NULL); + if(unlikely(ret != 0)) + error("RW_LOCK: failed to initialize lock (code %d)", ret); + return ret; +} + +static inline int __netdata_rwlock_rdlock(netdata_rwlock_t *rwlock) { + int ret = pthread_rwlock_rdlock(rwlock); + if(unlikely(ret != 0)) + error("RW_LOCK: failed to obtain read lock (code %d)", ret); + return ret; +} + +static inline int __netdata_rwlock_wrlock(netdata_rwlock_t *rwlock) { + int ret = pthread_rwlock_wrlock(rwlock); + if(unlikely(ret != 0)) + error("RW_LOCK: failed to obtain write lock (code %d)", ret); + return ret; +} + +static inline int __netdata_rwlock_unlock(netdata_rwlock_t *rwlock) { + int ret = pthread_rwlock_unlock(rwlock); + if(unlikely(ret != 0)) + error("RW_LOCK: failed to release lock (code %d)", ret); + return ret; +} + +static inline int __netdata_rwlock_tryrdlock(netdata_rwlock_t *rwlock) { + int ret = pthread_rwlock_tryrdlock(rwlock); + return ret; +} + +static inline int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock) { + int ret = pthread_rwlock_trywrlock(rwlock); + return ret; +} + + +#ifdef NETDATA_INTERNAL_CHECKS + +static inline int netdata_rwlock_destroy_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_destroy(0x%p) from %lu@%s, %s()", rwlock, line, file, function); + } + + int ret = __netdata_rwlock_destroy(rwlock); + + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_destroy(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +static inline int netdata_rwlock_init_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_init(0x%p) from %lu@%s, %s()", rwlock, line, file, function); + } + + int ret = __netdata_rwlock_init(rwlock); + + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_init(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +static inline int netdata_rwlock_rdlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_rdlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function); + } + + int ret = __netdata_rwlock_rdlock(rwlock); + + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_rdlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +static inline int netdata_rwlock_wrlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_wrlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function); + } + + int ret = __netdata_rwlock_wrlock(rwlock); + + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_wrlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +static inline int netdata_rwlock_unlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_unlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function); + } + + int ret = __netdata_rwlock_unlock(rwlock); + + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_unlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +static inline int netdata_rwlock_tryrdlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_tryrdlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function); + } + + int ret = __netdata_rwlock_tryrdlock(rwlock); + + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_tryrdlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +static inline int netdata_rwlock_trywrlock_debug( const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock) { + usec_t start = 0; + + if(unlikely(debug_flags & D_LOCKS)) { + start = now_boottime_usec(); + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_trywrlock(0x%p) from %lu@%s, %s()", rwlock, line, file, function); + } + + int ret = __netdata_rwlock_trywrlock(rwlock); + + debug(D_LOCKS, "RW_LOCK: netdata_rwlock_trywrlock(0x%p) = %d in %llu usec, from %lu@%s, %s()", rwlock, ret, now_boottime_usec() - start, line, file, function); + + return ret; +} + +#define netdata_rwlock_destroy(rwlock) netdata_rwlock_destroy_debug(__FILE__, __FUNCTION__, __LINE__, rwlock) +#define netdata_rwlock_init(rwlock) netdata_rwlock_init_debug(__FILE__, __FUNCTION__, __LINE__, rwlock) +#define netdata_rwlock_rdlock(rwlock) netdata_rwlock_rdlock_debug(__FILE__, __FUNCTION__, __LINE__, rwlock) +#define netdata_rwlock_wrlock(rwlock) netdata_rwlock_wrlock_debug(__FILE__, __FUNCTION__, __LINE__, rwlock) +#define netdata_rwlock_unlock(rwlock) netdata_rwlock_unlock_debug(__FILE__, __FUNCTION__, __LINE__, rwlock) +#define netdata_rwlock_tryrdlock(rwlock) netdata_rwlock_tryrdlock_debug(__FILE__, __FUNCTION__, __LINE__, rwlock) +#define netdata_rwlock_trywrlock(rwlock) netdata_rwlock_trywrlock_debug(__FILE__, __FUNCTION__, __LINE__, rwlock) + +#else // !NETDATA_INTERNAL_CHECKS + +#define netdata_rwlock_destroy(rwlock) __netdata_rwlock_destroy(rwlock) +#define netdata_rwlock_init(rwlock) __netdata_rwlock_init(rwlock) +#define netdata_rwlock_rdlock(rwlock) __netdata_rwlock_rdlock(rwlock) +#define netdata_rwlock_wrlock(rwlock) __netdata_rwlock_wrlock(rwlock) +#define netdata_rwlock_unlock(rwlock) __netdata_rwlock_unlock(rwlock) +#define netdata_rwlock_tryrdlock(rwlock) __netdata_rwlock_tryrdlock(rwlock) +#define netdata_rwlock_trywrlock(rwlock) __netdata_rwlock_trywrlock(rwlock) + +#endif // NETDATA_INTERNAL_CHECKS + +#endif //NETDATA_LOCKS_H @@ -1,7 +1,7 @@ #include "common.h" const char *program_name = ""; -unsigned long long debug_flags = DEBUG; +uint64_t debug_flags = DEBUG; int access_log_syslog = 1; int error_log_syslog = 1; @@ -257,8 +257,8 @@ void info_int( const char *file, const char *function, const unsigned long line, log_date(stderr); va_start( args, fmt ); - if(debug_flags) fprintf(stderr, "%s: INFO: (%04lu@%-10.10s:%-15.15s):", program_name, line, file, function); - else fprintf(stderr, "%s: INFO: ", program_name); + if(debug_flags) fprintf(stderr, "%s: INFO : (%04lu@%-10.10s:%-15.15s): ", program_name, line, file, function); + else fprintf(stderr, "%s: INFO : ", program_name); vfprintf( stderr, fmt, args ); va_end( args ); @@ -1,38 +1,40 @@ #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_PROCNETDEV_LOOP 0x00000020 -#define D_RRD_STATS 0x00000040 -#define D_WEB_CLIENT_ACCESS 0x00000080 -#define D_TC_LOOP 0x00000100 -#define D_DEFLATE 0x00000200 -#define D_CONFIG 0x00000400 -#define D_PLUGINSD 0x00000800 -#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_CGROUP 0x00100000 -#define D_REGISTRY 0x00200000 -#define D_VARIABLES 0x00400000 -#define D_HEALTH 0x00800000 -#define D_CONNECT_TO 0x01000000 -#define D_SYSTEM 0x80000000 +#define D_WEB_BUFFER 0x0000000000000001 +#define D_WEB_CLIENT 0x0000000000000002 +#define D_LISTENER 0x0000000000000004 +#define D_WEB_DATA 0x0000000000000008 +#define D_OPTIONS 0x0000000000000010 +#define D_PROCNETDEV_LOOP 0x0000000000000020 +#define D_RRD_STATS 0x0000000000000040 +#define D_WEB_CLIENT_ACCESS 0x0000000000000080 +#define D_TC_LOOP 0x0000000000000100 +#define D_DEFLATE 0x0000000000000200 +#define D_CONFIG 0x0000000000000400 +#define D_PLUGINSD 0x0000000000000800 +#define D_CHILDS 0x0000000000001000 +#define D_EXIT 0x0000000000002000 +#define D_CHECKS 0x0000000000004000 +#define D_NFACCT_LOOP 0x0000000000008000 +#define D_PROCFILE 0x0000000000010000 +#define D_RRD_CALLS 0x0000000000020000 +#define D_DICTIONARY 0x0000000000040000 +#define D_MEMORY 0x0000000000080000 +#define D_CGROUP 0x0000000000100000 +#define D_REGISTRY 0x0000000000200000 +#define D_VARIABLES 0x0000000000400000 +#define D_HEALTH 0x0000000000800000 +#define D_CONNECT_TO 0x0000000001000000 +#define D_RRDHOST 0x0000000002000000 +#define D_LOCKS 0x0000000004000000 +#define D_SYSTEM 0x8000000000000000 //#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS) //#define DEBUG 0xffffffff #define DEBUG (0) -extern unsigned long long debug_flags; +extern uint64_t debug_flags; extern const char *program_name; diff --git a/src/macos_fw.c b/src/macos_fw.c index a62aa7a7e..c47da52f1 100644 --- a/src/macos_fw.c +++ b/src/macos_fw.c @@ -136,12 +136,14 @@ int do_macos_iokit(int update_every, usec_t dt) { total_disk_writes += diskstat.bytes_write; } - st = rrdset_find_bytype("disk", diskstat.name); + st = rrdset_find_bytype_localhost("disk", diskstat.name); if (unlikely(!st)) { - st = rrdset_create("disk", diskstat.name, NULL, diskstat.name, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA); + st = rrdset_create_localhost("disk", diskstat.name, NULL, diskstat.name, "disk.io" + , "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every + , RRDSET_TYPE_AREA); - rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -161,13 +163,15 @@ int do_macos_iokit(int update_every, usec_t dt) { CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.writes); } - st = rrdset_find_bytype("disk_ops", diskstat.name); + st = rrdset_find_bytype_localhost("disk_ops", diskstat.name); if (unlikely(!st)) { - st = rrdset_create("disk_ops", diskstat.name, NULL, diskstat.name, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("disk_ops", diskstat.name, NULL, diskstat.name, "disk.ops" + , "Disk Completed I/O Operations", "operations/s", 2001 + , update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -187,12 +191,14 @@ int do_macos_iokit(int update_every, usec_t dt) { CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_write); } - st = rrdset_find_bytype("disk_util", diskstat.name); + st = rrdset_find_bytype_localhost("disk_util", diskstat.name); if (unlikely(!st)) { - st = rrdset_create("disk_util", diskstat.name, NULL, diskstat.name, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA); - st->isdetail = 1; + st = rrdset_create_localhost("disk_util", diskstat.name, NULL, diskstat.name, "disk.util" + , "Disk Utilization Time", "% of time working", 2004, update_every + , RRDSET_TYPE_AREA); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "utilization", NULL, 1, 10000000, RRDDIM_INCREMENTAL); + rrddim_add(st, "utilization", NULL, 1, 10000000, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -212,13 +218,15 @@ int do_macos_iokit(int update_every, usec_t dt) { CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_write); } - st = rrdset_find_bytype("disk_iotime", diskstat.name); + st = rrdset_find_bytype_localhost("disk_iotime", diskstat.name); if (unlikely(!st)) { - st = rrdset_create("disk_iotime", diskstat.name, NULL, diskstat.name, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("disk_iotime", diskstat.name, NULL, diskstat.name, "disk.iotime" + , "Disk Total I/O Time", "milliseconds/s", 2022, update_every + , RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_INCREMENTAL); - rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_INCREMENTAL); + rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -236,13 +244,15 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("disk_await", diskstat.name); + st = rrdset_find_bytype_localhost("disk_await", diskstat.name); if (unlikely(!st)) { - st = rrdset_create("disk_await", diskstat.name, NULL, diskstat.name, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("disk_await", diskstat.name, NULL, diskstat.name, "disk.await" + , "Average Completed I/O Operation Time", "ms per operation" + , 2005, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_ABSOLUTE); - rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_ABSOLUTE); + rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -254,13 +264,16 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("disk_avgsz", diskstat.name); + st = rrdset_find_bytype_localhost("disk_avgsz", diskstat.name); if (unlikely(!st)) { - st = rrdset_create("disk_avgsz", diskstat.name, NULL, diskstat.name, "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, 1, 1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE); + st = rrdset_create_localhost("disk_avgsz", diskstat.name, NULL, diskstat.name, "disk.avgsz" + , "Average Completed I/O Operation Bandwidth" + , "kilobytes per operation", 2006, update_every + , RRDSET_TYPE_AREA); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -272,12 +285,14 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("disk_svctm", diskstat.name); + st = rrdset_find_bytype_localhost("disk_svctm", diskstat.name); if (unlikely(!st)) { - st = rrdset_create("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm" + , "Average Service Time", "ms per operation", 2007 + , update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "svctm", NULL, 1, 1000000, RRDDIM_ABSOLUTE); + rrddim_add(st, "svctm", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -301,11 +316,12 @@ int do_macos_iokit(int update_every, usec_t dt) { } if (likely(do_io)) { - st = rrdset_find_bytype("system", "io"); + st = rrdset_find_bytype_localhost("system", "io"); if (unlikely(!st)) { - st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "in", NULL, 1, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150 + , update_every, RRDSET_TYPE_AREA); + rrddim_add(st, "in", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "out", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -340,17 +356,17 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------------- if (likely(do_space)) { - st = rrdset_find_bytype("disk_space", mntbuf[i].f_mntonname); + st = rrdset_find_bytype_localhost("disk_space", mntbuf[i].f_mntonname); if (unlikely(!st)) { snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); - st = rrdset_create("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023, - update_every, - RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname + , "disk.space", title, "GB", 2023, update_every + , RRDSET_TYPE_STACKED); - rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE); - rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE); + rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR, - RRDDIM_ABSOLUTE); + RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -363,15 +379,16 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------------- if (likely(do_inodes)) { - st = rrdset_find_bytype("disk_inodes", mntbuf[i].f_mntonname); + st = rrdset_find_bytype_localhost("disk_inodes", mntbuf[i].f_mntonname); if (unlikely(!st)) { snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); - st = rrdset_create("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "Inodes", 2024, - update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname + , "disk.inodes", title, "Inodes", 2024, update_every + , RRDSET_TYPE_STACKED); - 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); + rrddim_add(st, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -398,12 +415,13 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("net", ifa->ifa_name); + st = rrdset_find_bytype_localhost("net", ifa->ifa_name); if (unlikely(!st)) { - st = rrdset_create("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA); + st = rrdset_create_localhost("net", ifa->ifa_name, NULL, ifa->ifa_name, "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); + rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -413,15 +431,16 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("net_packets", ifa->ifa_name); + st = rrdset_find_bytype_localhost("net_packets", ifa->ifa_name); if (unlikely(!st)) { - st = rrdset_create("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "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_received", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "multicast_sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets" + , "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "multicast_received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "multicast_sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -433,13 +452,14 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("net_errors", ifa->ifa_name); + st = rrdset_find_bytype_localhost("net_errors", ifa->ifa_name); if (unlikely(!st)) { - st = rrdset_create("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors" + , "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -449,12 +469,13 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("net_drops", ifa->ifa_name); + st = rrdset_find_bytype_localhost("net_drops", ifa->ifa_name); if (unlikely(!st)) { - st = rrdset_create("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops" + , "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -463,14 +484,16 @@ int do_macos_iokit(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("net_events", ifa->ifa_name); + st = rrdset_find_bytype_localhost("net_events", ifa->ifa_name); if (unlikely(!st)) { - st = rrdset_create("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "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); + st = rrdset_create_localhost("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events" + , "Network Interface Events", "events/s", 7006, update_every + , RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/macos_mach_smi.c b/src/macos_mach_smi.c index d86a03220..da2825513 100644 --- a/src/macos_mach_smi.c +++ b/src/macos_mach_smi.c @@ -48,14 +48,15 @@ int do_macos_mach_smi(int update_every, usec_t dt) { error("DISABLED: system.cpu"); } else { - st = rrdset_find_bytype("system", "cpu"); + st = rrdset_find_bytype_localhost("system", "cpu"); if (unlikely(!st)) { - st = rrdset_create("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization" + , "percentage", 100, update_every, RRDSET_TYPE_STACKED); - rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); - rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "user", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "nice", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "system", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "idle", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rrddim_hide(st, "idle"); } else rrdset_next(st); @@ -84,18 +85,19 @@ int do_macos_mach_smi(int update_every, usec_t dt) { error("DISABLED: mem.pgfaults"); } else { if (likely(do_ram)) { - st = rrdset_find("system.ram"); + st = rrdset_find_localhost("system.ram"); if (unlikely(!st)) { - st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED); - - rrddim_add(st, "active", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); - rrddim_add(st, "wired", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); - rrddim_add(st, "throttled", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); - rrddim_add(st, "compressor", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); - rrddim_add(st, "inactive", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); - rrddim_add(st, "purgeable", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); - rrddim_add(st, "speculative", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); - rrddim_add(st, "free", NULL, system_pagesize, 1048576, RRDDIM_ABSOLUTE); + st = rrdset_create_localhost("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200 + , update_every, RRDSET_TYPE_STACKED); + + rrddim_add(st, "active", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "wired", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "throttled", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "compressor", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "inactive", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "purgeable", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "speculative", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "free", NULL, system_pagesize, 1048576, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -113,12 +115,13 @@ int do_macos_mach_smi(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_swapio)) { - st = rrdset_find("system.swapio"); + st = rrdset_find_localhost("system.swapio"); if (unlikely(!st)) { - st = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA); + st = rrdset_create_localhost("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250 + , update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "in", NULL, system_pagesize, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "in", NULL, system_pagesize, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -130,20 +133,21 @@ int do_macos_mach_smi(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_pgfaults)) { - st = rrdset_find("mem.pgfaults"); + st = rrdset_find_localhost("mem.pgfaults"); if (unlikely(!st)) { - st = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; - - rrddim_add(st, "memory", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "cow", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "pagein", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "pageout", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "compress", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "decompress", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "zero_fill", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "reactivate", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "purge", NULL, 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults" + , "page faults/s", 500, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "memory", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "cow", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "pagein", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "pageout", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "compress", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "decompress", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "zero_fill", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "reactivate", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "purge", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/macos_sysctl.c b/src/macos_sysctl.c index 955b70757..af229fb61 100644 --- a/src/macos_sysctl.c +++ b/src/macos_sysctl.c @@ -24,8 +24,6 @@ #define MIN_LOADAVG_UPDATE_EVERY 5 int do_macos_sysctl(int update_every, usec_t dt) { - (void)dt; - static int do_loadavg = -1, do_swap = -1, do_bandwidth = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1, do_ecn = -1, do_tcpext_syscookies = -1, do_tcpext_ofo = -1, do_tcpext_connaborts = -1, @@ -43,10 +41,10 @@ int do_macos_sysctl(int update_every, usec_t dt) { do_tcp_packets = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP packets", 1); do_tcp_errors = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP errors", 1); do_tcp_handshake = config_get_boolean("plugin:macos:sysctl", "ipv4 TCP handshake issues", 1); - do_ecn = config_get_boolean_ondemand("plugin:macos:sysctl", "ECN packets", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_syscookies = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_ofo = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_connaborts = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND); + do_ecn = config_get_boolean_ondemand("plugin:macos:sysctl", "ECN packets", CONFIG_BOOLEAN_AUTO); + do_tcpext_syscookies = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP SYN cookies", CONFIG_BOOLEAN_AUTO); + do_tcpext_ofo = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP out-of-order queue", CONFIG_BOOLEAN_AUTO); + do_tcpext_connaborts = config_get_boolean_ondemand("plugin:macos:sysctl", "TCP connection aborts", CONFIG_BOOLEAN_AUTO); do_udp_packets = config_get_boolean("plugin:macos:sysctl", "ipv4 UDP packets", 1); do_udp_errors = config_get_boolean("plugin:macos:sysctl", "ipv4 UDP errors", 1); do_icmp_packets = config_get_boolean("plugin:macos:sysctl", "ipv4 ICMP packets", 1); @@ -55,17 +53,17 @@ int do_macos_sysctl(int update_every, usec_t dt) { do_ip_fragsout = config_get_boolean("plugin:macos:sysctl", "ipv4 fragments sent", 1); do_ip_fragsin = config_get_boolean("plugin:macos:sysctl", "ipv4 fragments assembly", 1); do_ip_errors = config_get_boolean("plugin:macos:sysctl", "ipv4 errors", 1); - do_ip6_packets = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND); - do_ip6_fragsout = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND); - do_ip6_fragsin = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND); - do_ip6_errors = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6 = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_redir = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_errors = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp errors", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_echos = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp echos", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_router = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp router", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_neighbor = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND); - do_icmp6_types = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp types", CONFIG_ONDEMAND_ONDEMAND); + do_ip6_packets = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 packets", CONFIG_BOOLEAN_AUTO); + do_ip6_fragsout = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments sent", CONFIG_BOOLEAN_AUTO); + do_ip6_fragsin = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 fragments assembly", CONFIG_BOOLEAN_AUTO); + do_ip6_errors = config_get_boolean_ondemand("plugin:macos:sysctl", "ipv6 errors", CONFIG_BOOLEAN_AUTO); + do_icmp6 = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp", CONFIG_BOOLEAN_AUTO); + do_icmp6_redir = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp redirects", CONFIG_BOOLEAN_AUTO); + do_icmp6_errors = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp errors", CONFIG_BOOLEAN_AUTO); + do_icmp6_echos = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp echos", CONFIG_BOOLEAN_AUTO); + do_icmp6_router = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp router", CONFIG_BOOLEAN_AUTO); + do_icmp6_neighbor = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp neighbor", CONFIG_BOOLEAN_AUTO); + do_icmp6_types = config_get_boolean_ondemand("plugin:macos:sysctl", "icmp types", CONFIG_BOOLEAN_AUTO); do_uptime = config_get_boolean("plugin:macos:sysctl", "system uptime", 1); } @@ -77,7 +75,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { size_t size; // NEEDED BY: do_loadavg - static usec_t last_loadavg_usec = 0; + static usec_t next_loadavg_dt = 0; struct loadavg sysload; // NEEDED BY: do_swap @@ -210,19 +208,21 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (last_loadavg_usec <= dt) { + if (next_loadavg_dt <= dt) { if (likely(do_loadavg)) { - if (unlikely(GETSYSCTL("vm.loadavg", sysload))) { + if (unlikely(GETSYSCTL_BY_NAME("vm.loadavg", sysload))) { do_loadavg = 0; error("DISABLED: system.load"); } else { - st = rrdset_find_bytype("system", "load"); + st = rrdset_find_bytype_localhost("system", "load"); if (unlikely(!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); + st = rrdset_create_localhost("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, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "load5", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "load15", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -233,24 +233,25 @@ int do_macos_sysctl(int update_every, usec_t dt) { } } - last_loadavg_usec = st->update_every * USEC_PER_SEC; + next_loadavg_dt = st->update_every * USEC_PER_SEC; } - else last_loadavg_usec -= dt; + else next_loadavg_dt -= dt; // -------------------------------------------------------------------- if (likely(do_swap)) { - if (unlikely(GETSYSCTL("vm.swapusage", swap_usage))) { + if (unlikely(GETSYSCTL_BY_NAME("vm.swapusage", swap_usage))) { do_swap = 0; error("DISABLED: system.swap"); } else { - st = rrdset_find("system.swap"); + st = rrdset_find_localhost("system.swap"); if (unlikely(!st)) { - st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED); - st->isdetail = 1; + st = rrdset_create_localhost("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201 + , update_every, RRDSET_TYPE_STACKED); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "free", NULL, 1, 1048576, RRDDIM_ABSOLUTE); - rrddim_add(st, "used", NULL, 1, 1048576, RRDDIM_ABSOLUTE); + rrddim_add(st, "free", NULL, 1, 1048576, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "used", NULL, 1, 1048576, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -293,12 +294,13 @@ int do_macos_sysctl(int update_every, usec_t dt) { iftot.ift_obytes += if2m->ifm_data.ifi_obytes; } } - st = rrdset_find("system.ipv4"); + st = rrdset_find_localhost("system.ipv4"); if (unlikely(!st)) { - st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA); + st = rrdset_create_localhost("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s" + , 500, update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "InOctets", "received", 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutOctets", "sent", -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -313,7 +315,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html if (likely(do_tcp_packets || do_tcp_errors || do_tcp_handshake || do_tcpext_connaborts || do_tcpext_ofo || do_tcpext_syscookies || do_ecn)) { - if (unlikely(GETSYSCTL("net.inet.tcp.stats", tcpstat))){ + if (unlikely(GETSYSCTL_BY_NAME("net.inet.tcp.stats", tcpstat))){ do_tcp_packets = 0; error("DISABLED: ipv4.tcppackets"); do_tcp_errors = 0; @@ -330,14 +332,13 @@ int do_macos_sysctl(int update_every, usec_t dt) { error("DISABLED: ipv4.ecnpkts"); } else { if (likely(do_tcp_packets)) { - st = rrdset_find("ipv4.tcppackets"); + st = rrdset_find_localhost("ipv4.tcppackets"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", - "packets/s", - 2600, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets" + , "packets/s", 2600, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InSegs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutSegs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -349,16 +350,15 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_tcp_errors)) { - st = rrdset_find("ipv4.tcperrors"); + st = rrdset_find_localhost("ipv4.tcperrors"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "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, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv4", "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", "packets/s" + , 2700, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "InErrs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "RetransSegs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -371,17 +371,16 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_tcp_handshake)) { - st = rrdset_find("ipv4.tcphandshake"); + st = rrdset_find_localhost("ipv4.tcphandshake"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "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, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv4", "tcphandshake", NULL, "tcp", NULL, "IPv4 TCP Handshake Issues" + , "events/s", 2900, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "EstabResets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "AttemptFails", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -394,16 +393,17 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop))) { - do_tcpext_connaborts = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.tcpconnaborts"); + if (do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_rcvpackafterwin || tcpstat.tcps_rcvafterclose || tcpstat.tcps_rcvmemdrop || tcpstat.tcps_persistdrop))) { + do_tcpext_connaborts = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.tcpconnaborts"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts" + , "connections/s", 3010, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnClose", "userclosed", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnClose", "userclosed", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -416,13 +416,14 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && tcpstat.tcps_rcvoopack)) { - do_tcpext_ofo = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.tcpofo"); + if (do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && tcpstat.tcps_rcvoopack)) { + do_tcpext_ofo = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.tcpofo"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue" + , "packets/s", 3050, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -432,16 +433,17 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) { - do_tcpext_syscookies = CONFIG_ONDEMAND_YES; + if (do_tcpext_syscookies == CONFIG_BOOLEAN_YES || (do_tcpext_syscookies == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_sc_sendcookie || tcpstat.tcps_sc_recvcookie || tcpstat.tcps_sc_zonefail))) { + do_tcpext_syscookies = CONFIG_BOOLEAN_YES; - st = rrdset_find("ipv4.tcpsyncookies"); + st = rrdset_find_localhost("ipv4.tcpsyncookies"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies" + , "packets/s", 3100, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -453,15 +455,16 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (tcpstat.tcps_ecn_recv_ce || tcpstat.tcps_ecn_not_supported))) { - do_ecn = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.ecnpkts"); + if (do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && (tcpstat.tcps_ecn_recv_ce || tcpstat.tcps_ecn_not_supported))) { + do_ecn = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.ecnpkts"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics" + , "packets/s", 8700, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -477,20 +480,20 @@ int do_macos_sysctl(int update_every, usec_t dt) { // see http://net-snmp.sourceforge.net/docs/mibs/udp.html if (likely(do_udp_packets || do_udp_errors)) { - if (unlikely(GETSYSCTL("net.inet.udp.stats", udpstat))) { + if (unlikely(GETSYSCTL_BY_NAME("net.inet.udp.stats", udpstat))) { do_udp_packets = 0; error("DISABLED: ipv4.udppackets"); do_udp_errors = 0; error("DISABLED: ipv4.udperrors"); } else { if (likely(do_udp_packets)) { - st = rrdset_find("ipv4.udppackets"); + st = rrdset_find_localhost("ipv4.udppackets"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", - "packets/s", 2601, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets" + , "packets/s", 2601, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -502,17 +505,17 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_udp_errors)) { - st = rrdset_find("ipv4.udperrors"); + st = rrdset_find_localhost("ipv4.udperrors"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "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, "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); + st = rrdset_create_localhost("ipv4", "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s" + , 2701, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -529,7 +532,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_icmp_packets || do_icmpmsg)) { - if (unlikely(GETSYSCTL("net.inet.icmp.stats", icmpstat))) { + if (unlikely(GETSYSCTL_BY_NAME("net.inet.icmp.stats", icmpstat))) { do_icmp_packets = 0; error("DISABLED: ipv4.icmp"); error("DISABLED: ipv4.icmp_errors"); @@ -545,14 +548,13 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_icmp_packets)) { - st = rrdset_find("ipv4.icmp"); + st = rrdset_find_localhost("ipv4.icmp"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s", - 2602, - update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s" + , 2602, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InMsgs", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutMsgs", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -563,15 +565,14 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find("ipv4.icmp_errors"); + st = rrdset_find_localhost("ipv4.icmp_errors"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors", - "packets/s", - 2603, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors" + , "packets/s", 2603, 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, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -585,15 +586,15 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_icmpmsg)) { - st = rrdset_find("ipv4.icmpmsg"); + st = rrdset_find_localhost("ipv4.icmpmsg"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages", - "packets/s", 2604, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages" + , "packets/s", 2604, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InEchoReps", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InEchoReps", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutEchoReps", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -611,7 +612,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html if (likely(do_ip_packets || do_ip_fragsout || do_ip_fragsin || do_ip_errors)) { - if (unlikely(GETSYSCTL("net.inet.ip.stats", ipstat))) { + if (unlikely(GETSYSCTL_BY_NAME("net.inet.ip.stats", ipstat))) { do_ip_packets = 0; error("DISABLED: ipv4.packets"); do_ip_fragsout = 0; @@ -622,15 +623,15 @@ int do_macos_sysctl(int update_every, usec_t dt) { error("DISABLED: ipv4.errors"); } else { if (likely(do_ip_packets)) { - st = rrdset_find("ipv4.packets"); + st = rrdset_find_localhost("ipv4.packets"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", - 3000, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s" + , 3000, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InReceives", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutRequests", "sent", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InDelivers", "delivered", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InReceives", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InDelivers", "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -644,15 +645,15 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_ip_fragsout)) { - st = rrdset_find("ipv4.fragsout"); + st = rrdset_find_localhost("ipv4.fragsout"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", - "packets/s", 3010, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("ipv4", "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent" + , "packets/s", 3010, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "FragOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "FragFails", "failed", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "FragCreates", "created", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -665,16 +666,15 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_ip_fragsin)) { - st = rrdset_find("ipv4.fragsin"); + st = rrdset_find_localhost("ipv4.fragsin"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "fragsin", NULL, "fragments", NULL, - "IPv4 Fragments Reassembly", - "packets/s", 3011, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; - - rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "ReasmFails", "failed", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "ReasmReqds", "all", 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv4", "fragsin", NULL, "fragments", NULL, "IPv4 Fragments Reassembly" + , "packets/s", 3011, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -687,21 +687,20 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_ip_errors)) { - st = rrdset_find("ipv4.errors"); + st = rrdset_find_localhost("ipv4.errors"); if (unlikely(!st)) { - st = rrdset_create("ipv4", "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s", - 3002, - update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("ipv4", "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s" + , 3002, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -719,7 +718,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_ip6_packets || do_ip6_fragsout || do_ip6_fragsin || do_ip6_errors)) { - if (unlikely(GETSYSCTL("net.inet6.ip6.stats", ip6stat))) { + if (unlikely(GETSYSCTL_BY_NAME("net.inet6.ip6.stats", ip6stat))) { do_ip6_packets = 0; error("DISABLED: ipv6.packets"); do_ip6_fragsout = 0; @@ -729,19 +728,19 @@ int do_macos_sysctl(int update_every, usec_t dt) { do_ip6_errors = 0; error("DISABLED: ipv6.errors"); } else { - if (do_ip6_packets == CONFIG_ONDEMAND_YES || (do_ip6_packets == CONFIG_ONDEMAND_ONDEMAND && + if (do_ip6_packets == CONFIG_BOOLEAN_YES || (do_ip6_packets == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_localout || ip6stat.ip6s_total || ip6stat.ip6s_forward || ip6stat.ip6s_delivered))) { - do_ip6_packets = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.packets"); + do_ip6_packets = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.packets"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, - update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv6", "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); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "forwarded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "delivers", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -754,19 +753,19 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_ip6_fragsout == CONFIG_ONDEMAND_YES || (do_ip6_fragsout == CONFIG_ONDEMAND_ONDEMAND && + if (do_ip6_fragsout == CONFIG_BOOLEAN_YES || (do_ip6_fragsout == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_fragmented || ip6stat.ip6s_cantfrag || ip6stat.ip6s_ofragments))) { - do_ip6_fragsout = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.fragsout"); + do_ip6_fragsout = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.fragsout"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", - "packets/s", 3010, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("ipv6", "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent" + , "packets/s", 3010, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - 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); + rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -778,20 +777,20 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_ip6_fragsin == CONFIG_ONDEMAND_YES || (do_ip6_fragsin == CONFIG_ONDEMAND_ONDEMAND && + if (do_ip6_fragsin == CONFIG_BOOLEAN_YES || (do_ip6_fragsin == CONFIG_BOOLEAN_AUTO && (ip6stat.ip6s_reassembled || ip6stat.ip6s_fragdropped || ip6stat.ip6s_fragtimeout || ip6stat.ip6s_fragments))) { - do_ip6_fragsin = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.fragsin"); + do_ip6_fragsin = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.fragsin"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "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); + st = rrdset_create_localhost("ipv6", "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly" + , "packets/s", 3011, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "timeout", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -804,7 +803,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_ip6_errors == CONFIG_ONDEMAND_YES || (do_ip6_errors == CONFIG_ONDEMAND_ONDEMAND && ( + if (do_ip6_errors == CONFIG_BOOLEAN_YES || (do_ip6_errors == CONFIG_BOOLEAN_AUTO && ( ip6stat.ip6s_toosmall || ip6stat.ip6s_odropped || ip6stat.ip6s_badoptions || @@ -814,22 +813,22 @@ int do_macos_sysctl(int update_every, usec_t dt) { ip6stat.ip6s_tooshort || ip6stat.ip6s_cantforward || ip6stat.ip6s_noroute))) { - do_ip6_errors = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.errors"); + do_ip6_errors = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.errors"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, - update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("ipv6", "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s" + , 3002, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InAddrErrors", 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, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -851,7 +850,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_icmp6 || do_icmp6_redir || do_icmp6_errors || do_icmp6_echos || do_icmp6_router || do_icmp6_neighbor || do_icmp6_types)) { - if (unlikely(GETSYSCTL("net.inet6.icmp6.stats", icmp6stat))) { + if (unlikely(GETSYSCTL_BY_NAME("net.inet6.icmp6.stats", icmp6stat))) { do_icmp6 = 0; error("DISABLED: ipv6.icmp"); } else { @@ -860,15 +859,15 @@ int do_macos_sysctl(int update_every, usec_t dt) { icmp6_total.msgs_out += icmp6stat.icp6s_outhist[i]; } icmp6_total.msgs_in += icmp6stat.icp6s_badcode + icmp6stat.icp6s_badlen + icmp6stat.icp6s_checksum + icmp6stat.icp6s_tooshort; - if (do_icmp6 == CONFIG_ONDEMAND_YES || (do_icmp6 == CONFIG_ONDEMAND_ONDEMAND && (icmp6_total.msgs_in || icmp6_total.msgs_out))) { - do_icmp6 = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmp"); + if (do_icmp6 == CONFIG_BOOLEAN_YES || (do_icmp6 == CONFIG_BOOLEAN_AUTO && (icmp6_total.msgs_in || icmp6_total.msgs_out))) { + do_icmp6 = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.icmp"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", - "messages/s", 10000, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv6", "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); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -879,15 +878,15 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_icmp6_redir == CONFIG_ONDEMAND_YES || (do_icmp6_redir == CONFIG_ONDEMAND_ONDEMAND && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) { - do_icmp6_redir = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmpredir"); + if (do_icmp6_redir == CONFIG_BOOLEAN_YES || (do_icmp6_redir == CONFIG_BOOLEAN_AUTO && (icmp6stat.icp6s_inhist[ND_REDIRECT] || icmp6stat.icp6s_outhist[ND_REDIRECT]))) { + do_icmp6_redir = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.icmpredir"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", - "redirects/s", 10050, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv6", "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); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -898,7 +897,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_icmp6_errors == CONFIG_ONDEMAND_YES || (do_icmp6_errors == CONFIG_ONDEMAND_ONDEMAND && ( + if (do_icmp6_errors == CONFIG_BOOLEAN_YES || (do_icmp6_errors == CONFIG_BOOLEAN_AUTO && ( icmp6stat.icp6s_badcode || icmp6stat.icp6s_badlen || icmp6stat.icp6s_checksum || @@ -910,22 +909,23 @@ int do_macos_sysctl(int update_every, usec_t dt) { icmp6stat.icp6s_outhist[ICMP6_DST_UNREACH] || icmp6stat.icp6s_outhist[ICMP6_TIME_EXCEEDED] || icmp6stat.icp6s_outhist[ICMP6_PARAM_PROB]))) { - do_icmp6_errors = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmperrors"); + do_icmp6_errors = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.icmperrors"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "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, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv6", "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors" + , "errors/s", 10100, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InParmProblems", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -944,20 +944,21 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_icmp6_echos == CONFIG_ONDEMAND_YES || (do_icmp6_echos == CONFIG_ONDEMAND_ONDEMAND && ( + if (do_icmp6_echos == CONFIG_BOOLEAN_YES || (do_icmp6_echos == CONFIG_BOOLEAN_AUTO && ( icmp6stat.icp6s_inhist[ICMP6_ECHO_REQUEST] || icmp6stat.icp6s_outhist[ICMP6_ECHO_REQUEST] || icmp6stat.icp6s_inhist[ICMP6_ECHO_REPLY] || icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]))) { - do_icmp6_echos = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmpechos"); + do_icmp6_echos = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.icmpechos"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv6", "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); + rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -970,20 +971,21 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_icmp6_router == CONFIG_ONDEMAND_YES || (do_icmp6_router == CONFIG_ONDEMAND_ONDEMAND && ( + if (do_icmp6_router == CONFIG_BOOLEAN_YES || (do_icmp6_router == CONFIG_BOOLEAN_AUTO && ( icmp6stat.icp6s_inhist[ND_ROUTER_SOLICIT] || icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT] || icmp6stat.icp6s_inhist[ND_ROUTER_ADVERT] || icmp6stat.icp6s_outhist[ND_ROUTER_ADVERT]))) { - do_icmp6_router = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmprouter"); + do_icmp6_router = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.icmprouter"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv6", "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); + rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -996,20 +998,21 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_icmp6_neighbor == CONFIG_ONDEMAND_YES || (do_icmp6_neighbor == CONFIG_ONDEMAND_ONDEMAND && ( + if (do_icmp6_neighbor == CONFIG_BOOLEAN_YES || (do_icmp6_neighbor == CONFIG_BOOLEAN_AUTO && ( icmp6stat.icp6s_inhist[ND_NEIGHBOR_SOLICIT] || icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT] || icmp6stat.icp6s_inhist[ND_NEIGHBOR_ADVERT] || icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]))) { - do_icmp6_neighbor = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmpneighbor"); + do_icmp6_neighbor = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.icmpneighbor"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv6", "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); + rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -1022,7 +1025,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if (do_icmp6_types == CONFIG_ONDEMAND_YES || (do_icmp6_types == CONFIG_ONDEMAND_ONDEMAND && ( + if (do_icmp6_types == CONFIG_BOOLEAN_YES || (do_icmp6_types == CONFIG_BOOLEAN_AUTO && ( icmp6stat.icp6s_inhist[1] || icmp6stat.icp6s_inhist[128] || icmp6stat.icp6s_inhist[129] || @@ -1033,22 +1036,22 @@ int do_macos_sysctl(int update_every, usec_t dt) { icmp6stat.icp6s_outhist[133] || icmp6stat.icp6s_outhist[135] || icmp6stat.icp6s_outhist[136]))) { - do_icmp6_types = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv6.icmptypes"); + do_icmp6_types = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv6.icmptypes"); if (unlikely(!st)) { - st = rrdset_create("ipv6", "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); + st = rrdset_create_localhost("ipv6", "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types" + , "messages/s", 10700, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "InType1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InType128", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InType129", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InType136", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType1", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType128", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType129", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -1070,16 +1073,17 @@ int do_macos_sysctl(int update_every, usec_t dt) { // -------------------------------------------------------------------- if (likely(do_uptime)) { - if (unlikely(GETSYSCTL("kern.boottime", boot_time))) { + if (unlikely(GETSYSCTL_BY_NAME("kern.boottime", boot_time))) { do_uptime = 0; error("DISABLED: system.uptime"); } else { clock_gettime(CLOCK_REALTIME, &cur_time); - st = rrdset_find("system.uptime"); + st = rrdset_find_localhost("system.uptime"); if(unlikely(!st)) { - st = rrdset_create("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "uptime", NULL, 1, 1, RRDDIM_ABSOLUTE); + st = rrdset_create_localhost("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000 + , update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "uptime", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -1091,7 +1095,7 @@ int do_macos_sysctl(int update_every, usec_t dt) { return 0; } -int getsysctl(const char *name, void *ptr, size_t len) +int getsysctl_by_name(const char *name, void *ptr, size_t len) { size_t nlen = len; diff --git a/src/main.c b/src/main.c index ca134fcb0..a72585e28 100644 --- a/src/main.c +++ b/src/main.c @@ -10,7 +10,7 @@ void netdata_cleanup_and_exit(int ret) { debug(D_EXIT, "Called: netdata_cleanup_and_exit()"); // save the database - rrdset_save_all(); + rrdhost_save_all(); // unlink the pid if(pidfile[0]) { @@ -23,7 +23,8 @@ void netdata_cleanup_and_exit(int ret) { //kill_childs(); // free database - rrdset_free_all(); + sleep(2); + rrdhost_free_all(); #endif info("netdata exiting. Bye bye..."); @@ -34,49 +35,55 @@ struct netdata_static_thread 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", CONFIG_SECTION_PLUGINS, "nfacct", 1, NULL, NULL, nfacct_main}, #endif - {"tc", "plugins", "tc", 1, NULL, NULL, tc_main}, - {"idlejitter", "plugins", "idlejitter", 1, NULL, NULL, cpuidlejitter_main}, + {"tc", CONFIG_SECTION_PLUGINS, "tc", 1, NULL, NULL, tc_main}, + {"idlejitter", CONFIG_SECTION_PLUGINS, "idlejitter", 1, NULL, NULL, cpuidlejitter_main}, #if defined(__FreeBSD__) - {"freebsd", "plugins", "freebsd", 1, NULL, NULL, freebsd_main}, + {"freebsd", CONFIG_SECTION_PLUGINS, "freebsd", 1, NULL, NULL, freebsd_main}, #elif defined(__APPLE__) - {"macos", "plugins", "macos", 1, NULL, NULL, macos_main}, + {"macos", CONFIG_SECTION_PLUGINS, "macos", 1, NULL, NULL, macos_main}, #else - {"proc", "plugins", "proc", 1, NULL, NULL, proc_main}, - {"diskspace", "plugins", "diskspace", 1, NULL, NULL, proc_diskspace_main}, + {"proc", CONFIG_SECTION_PLUGINS, "proc", 1, NULL, NULL, proc_main}, + {"diskspace", CONFIG_SECTION_PLUGINS, "diskspace", 1, NULL, NULL, proc_diskspace_main}, + {"cgroups", CONFIG_SECTION_PLUGINS, "cgroups", 1, NULL, NULL, cgroups_main}, #endif /* __FreeBSD__, __APPLE__*/ - {"cgroups", "plugins", "cgroups", 1, NULL, NULL, cgroups_main}, - {"check", "plugins", "checks", 0, NULL, NULL, checks_main}, - {"backends", NULL, NULL, 1, NULL, NULL, backends_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} + {"check", CONFIG_SECTION_PLUGINS, "checks", 0, NULL, NULL, checks_main}, + {"backends", NULL, NULL, 1, NULL, NULL, backends_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}, + {"push-metrics", NULL, NULL, 0, NULL, NULL, rrdpush_sender_thread}, + {NULL, NULL, NULL, 0, NULL, NULL, NULL} }; void web_server_threading_selection(void) { - int threaded = config_get_boolean("global", "multi threaded web server", 1); + web_server_mode = web_server_mode_id(config_get(CONFIG_SECTION_WEB, "mode", web_server_mode_name(web_server_mode))); + + int multi_threaded = (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED); + int single_threaded = (web_server_mode == WEB_SERVER_MODE_SINGLE_THREADED); 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; + static_threads[i].enabled = multi_threaded; if(static_threads[i].start_routine == socket_listen_main_single_threaded) - static_threads[i].enabled = threaded?0:1; + static_threads[i].enabled = single_threaded; } - web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS); + web_client_timeout = (int) config_get_number(CONFIG_SECTION_WEB, "disconnect idle 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); + respect_web_browser_do_not_track_policy = config_get_boolean(CONFIG_SECTION_WEB, "respect do not track policy", respect_web_browser_do_not_track_policy); + web_x_frame_options = config_get(CONFIG_SECTION_WEB, "x-frame-options response header", ""); + if(!*web_x_frame_options) web_x_frame_options = NULL; #ifdef NETDATA_WITH_ZLIB - web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip); + web_enable_gzip = config_get_boolean(CONFIG_SECTION_WEB, "enable gzip compression", web_enable_gzip); - char *s = config_get("global", "web compression strategy", "default"); + char *s = config_get(CONFIG_SECTION_WEB, "gzip compression strategy", "default"); if(!strcmp(s, "default")) web_gzip_strategy = Z_DEFAULT_STRATEGY; else if(!strcmp(s, "filtered")) @@ -92,7 +99,7 @@ void web_server_threading_selection(void) { web_gzip_strategy = Z_DEFAULT_STRATEGY; } - web_gzip_level = (int)config_get_number("global", "web compression level", 3); + web_gzip_level = (int)config_get_number(CONFIG_SECTION_WEB, "gzip 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; @@ -218,12 +225,12 @@ struct option_def options[] = { { 'h', "Display this help message.", NULL, NULL}, { 'P', "File to save a pid while running.", "filename", "do not save pid to a file"}, { 'i', "The IP address to listen to.", "IP", "all IP addresses IPv4 and IPv6"}, - { 'k', "Check health configuration and exit.", NULL, NULL}, { 'p', "API/Web port to use.", "port", "19999"}, { 's', "Prefix for /proc and /sys (for containers).", "path", "no prefix"}, { 't', "The internal clock of netdata.", "seconds", "1"}, { 'u', "Run as user.", "username", "netdata"}, { 'v', "Print netdata version and exit.", NULL, NULL}, + { 'V', "Print netdata version and exit.", NULL, NULL}, { 'W', "See Advanced options below.", "options", NULL}, }; @@ -256,7 +263,7 @@ void help(int exitcode) { " +----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+--->\n" "\n" " Copyright (C) 2016-2017, Costa Tsaousis <costa@tsaousis.gr>\n" - " Released under GNU Public License v3 or later.\n" + " Released under GNU General Public License v3 or later.\n" " All rights reserved.\n" "\n" " Home Page : https://my-netdata.io\n" @@ -332,10 +339,191 @@ static const char *verify_required_directory(const char *dir) { return dir; } -int main(int argc, char **argv) -{ - char *hostname = "localhost"; - int i, check_config = 0; +void log_init(void) { + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s/debug.log", netdata_configured_log_dir); + stdout_filename = config_get(CONFIG_SECTION_GLOBAL, "debug log", filename); + + snprintfz(filename, FILENAME_MAX, "%s/error.log", netdata_configured_log_dir); + stderr_filename = config_get(CONFIG_SECTION_GLOBAL, "error log", filename); + + snprintfz(filename, FILENAME_MAX, "%s/access.log", netdata_configured_log_dir); + stdaccess_filename = config_get(CONFIG_SECTION_GLOBAL, "access log", filename); + + error_log_throttle_period_backup = + error_log_throttle_period = config_get_number(CONFIG_SECTION_GLOBAL, "errors flood protection period", error_log_throttle_period); + error_log_errors_per_period = (unsigned long)config_get_number(CONFIG_SECTION_GLOBAL, "errors to trigger flood protection", (long long int)error_log_errors_per_period); + + setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get(CONFIG_SECTION_GLOBAL, "errors flood protection period" , ""), 1); + setenv("NETDATA_ERRORS_PER_PERIOD", config_get(CONFIG_SECTION_GLOBAL, "errors to trigger flood protection", ""), 1); +} + +static void backwards_compatible_config() { + // allow existing configurations to work with the current version of netdata + + if(config_exists(CONFIG_SECTION_GLOBAL, "multi threaded web server")) { + int mode = config_get_boolean(CONFIG_SECTION_GLOBAL, "multi threaded web server", 1); + web_server_mode = (mode)?WEB_SERVER_MODE_MULTI_THREADED:WEB_SERVER_MODE_SINGLE_THREADED; + } + + // move [global] options to the [web] section + config_move(CONFIG_SECTION_GLOBAL, "http port listen backlog", + CONFIG_SECTION_WEB, "listen backlog"); + + config_move(CONFIG_SECTION_GLOBAL, "bind socket to IP", + CONFIG_SECTION_WEB, "bind to"); + + config_move(CONFIG_SECTION_GLOBAL, "bind to", + CONFIG_SECTION_WEB, "bind to"); + + config_move(CONFIG_SECTION_GLOBAL, "port", + CONFIG_SECTION_WEB, "default port"); + + config_move(CONFIG_SECTION_GLOBAL, "default port", + CONFIG_SECTION_WEB, "default port"); + + config_move(CONFIG_SECTION_GLOBAL, "disconnect idle web clients after seconds", + CONFIG_SECTION_WEB, "disconnect idle clients after seconds"); + + config_move(CONFIG_SECTION_GLOBAL, "respect web browser do not track policy", + CONFIG_SECTION_WEB, "respect do not track policy"); + + config_move(CONFIG_SECTION_GLOBAL, "web x-frame-options header", + CONFIG_SECTION_WEB, "x-frame-options response header"); + + config_move(CONFIG_SECTION_GLOBAL, "enable web responses gzip compression", + CONFIG_SECTION_WEB, "enable gzip compression"); + + config_move(CONFIG_SECTION_GLOBAL, "web compression strategy", + CONFIG_SECTION_WEB, "gzip compression strategy"); + + config_move(CONFIG_SECTION_GLOBAL, "web compression level", + CONFIG_SECTION_WEB, "gzip compression level"); + + config_move(CONFIG_SECTION_GLOBAL, "web files owner", + CONFIG_SECTION_WEB, "web files owner"); + + config_move(CONFIG_SECTION_GLOBAL, "web files group", + CONFIG_SECTION_WEB, "web files group"); +} + +static void get_netdata_configured_variables() { + backwards_compatible_config(); + + // ------------------------------------------------------------------------ + // get the hostname + + char buf[HOSTNAME_MAX + 1]; + if(gethostname(buf, HOSTNAME_MAX) == -1) + error("Cannot get machine hostname."); + + netdata_configured_hostname = config_get(CONFIG_SECTION_GLOBAL, "hostname", buf); + debug(D_OPTIONS, "hostname set to '%s'", netdata_configured_hostname); + + netdata_configured_hostname = config_get(CONFIG_SECTION_GLOBAL, "hostname", CONFIG_DIR); + + // ------------------------------------------------------------------------ + // get default database size + + default_rrd_history_entries = (int) config_get_number(CONFIG_SECTION_GLOBAL, "history", align_entries_to_pagesize(default_rrd_memory_mode, RRD_DEFAULT_HISTORY_ENTRIES)); + + long h = align_entries_to_pagesize(default_rrd_memory_mode, default_rrd_history_entries); + if(h != default_rrd_history_entries) { + config_set_number(CONFIG_SECTION_GLOBAL, "history", h); + default_rrd_history_entries = (int)h; + } + + if(default_rrd_history_entries < 5 || default_rrd_history_entries > RRD_HISTORY_ENTRIES_MAX) { + error("Invalid history entries %d given. Defaulting to %d.", default_rrd_history_entries, RRD_DEFAULT_HISTORY_ENTRIES); + default_rrd_history_entries = RRD_DEFAULT_HISTORY_ENTRIES; + } + + // ------------------------------------------------------------------------ + // get default database update frequency + + default_rrd_update_every = (int) config_get_number(CONFIG_SECTION_GLOBAL, "update every", UPDATE_EVERY); + if(default_rrd_update_every < 1 || default_rrd_update_every > 600) { + error("Invalid data collection frequency (update every) %d given. Defaulting to %d.", default_rrd_update_every, UPDATE_EVERY_MAX); + default_rrd_update_every = UPDATE_EVERY; + } + + // ------------------------------------------------------------------------ + // let the plugins know the min update_every + + // get system paths + netdata_configured_config_dir = config_get(CONFIG_SECTION_GLOBAL, "config directory", CONFIG_DIR); + netdata_configured_log_dir = config_get(CONFIG_SECTION_GLOBAL, "log directory", LOG_DIR); + netdata_configured_plugins_dir = config_get(CONFIG_SECTION_GLOBAL, "plugins directory", PLUGINS_DIR); + netdata_configured_web_dir = config_get(CONFIG_SECTION_GLOBAL, "web files directory", WEB_DIR); + netdata_configured_cache_dir = config_get(CONFIG_SECTION_GLOBAL, "cache directory", CACHE_DIR); + netdata_configured_varlib_dir = config_get(CONFIG_SECTION_GLOBAL, "lib directory", VARLIB_DIR); + netdata_configured_home_dir = config_get(CONFIG_SECTION_GLOBAL, "home directory", CACHE_DIR); + + // ------------------------------------------------------------------------ + // get default memory mode for the database + + default_rrd_memory_mode = rrd_memory_mode_id(config_get(CONFIG_SECTION_GLOBAL, "memory mode", rrd_memory_mode_name(default_rrd_memory_mode))); + + // ------------------------------------------------------------------------ + + netdata_configured_host_prefix = config_get(CONFIG_SECTION_GLOBAL, "host access prefix", ""); + + // -------------------------------------------------------------------- + // get KSM settings + +#ifdef MADV_MERGEABLE + enable_ksm = config_get_boolean(CONFIG_SECTION_GLOBAL, "memory deduplication (ksm)", enable_ksm); +#endif + + // -------------------------------------------------------------------- + // get various system parameters + + get_system_HZ(); + get_system_cpus(); + get_system_pid_max(); +} + +void set_global_environment() { + { + char b[16]; + snprintfz(b, 15, "%d", default_rrd_update_every); + setenv("NETDATA_UPDATE_EVERY", b, 1); + } + + setenv("NETDATA_HOSTNAME" , netdata_configured_hostname, 1); + setenv("NETDATA_CONFIG_DIR" , verify_required_directory(netdata_configured_config_dir), 1); + setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_plugins_dir), 1); + setenv("NETDATA_WEB_DIR" , verify_required_directory(netdata_configured_web_dir), 1); + setenv("NETDATA_CACHE_DIR" , verify_required_directory(netdata_configured_cache_dir), 1); + setenv("NETDATA_LIB_DIR" , verify_required_directory(netdata_configured_varlib_dir), 1); + setenv("NETDATA_LOG_DIR" , verify_required_directory(netdata_configured_log_dir), 1); + setenv("HOME" , verify_required_directory(netdata_configured_home_dir), 1); + setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 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); + + // set the path we need + 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(CONFIG_SECTION_PLUGINS, "PATH environment variable", path), 1); + + // python options + p = getenv("PYTHONPATH"); + if(!p) p = ""; + setenv("PYTHONPATH", config_get(CONFIG_SECTION_PLUGINS, "PYTHONPATH environment variable", p), 1); + + // disable buffering for python plugins + setenv("PYTHONUNBUFFERED", "1", 1); + + // switch to standard locale for plugins + setenv("LC_ALL", "C", 1); +} + +int main(int argc, char **argv) { + int i; int config_loaded = 0; int dont_fork = 0; size_t wanted_stacksize = 0, stacksize = 0; @@ -360,12 +548,12 @@ int main(int argc, char **argv) remove_option(i, &argc, argv); } else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) { - config_set("global", "host access prefix", argv[i+1]); + config_set(CONFIG_SECTION_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]); + config_set(CONFIG_SECTION_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); } @@ -395,7 +583,7 @@ int main(int argc, char **argv) while( (opt = getopt(argc, argv, optstring)) != -1 ) { switch(opt) { case 'c': - if(load_config(optarg, 1) != 1) { + if(config_load(optarg, 1) != 1) { error("Cannot load configuration file %s.", optarg); exit(1); } @@ -411,38 +599,42 @@ int main(int argc, char **argv) help(0); break; case 'i': - config_set("global", "bind to", optarg); - break; - case 'k': - dont_fork = 1; - check_config = 1; + config_set(CONFIG_SECTION_WEB, "bind to", optarg); break; case 'P': strncpy(pidfile, optarg, FILENAME_MAX); pidfile[FILENAME_MAX] = '\0'; break; case 'p': - config_set("global", "default port", optarg); + config_set(CONFIG_SECTION_GLOBAL, "default port", optarg); break; case 's': - config_set("global", "host access prefix", optarg); + config_set(CONFIG_SECTION_GLOBAL, "host access prefix", optarg); break; case 't': - config_set("global", "update every", optarg); + config_set(CONFIG_SECTION_GLOBAL, "update every", optarg); break; case 'u': - config_set("global", "run as user", optarg); + config_set(CONFIG_SECTION_GLOBAL, "run as user", optarg); break; case 'v': - // TODO: Outsource version to makefile which can compute version from git. - printf("netdata %s\n", VERSION); + case 'V': + printf("%s %s\n", program_name, program_version); return 0; case 'W': { char* stacksize_string = "stacksize="; char* debug_flags_string = "debug_flags="; if(strcmp(optarg, "unittest") == 0) { - rrd_update_every = 1; + default_rrd_update_every = 1; + default_rrd_memory_mode = RRD_MEMORY_MODE_RAM; + if(!config_loaded) config_load(NULL, 0); + get_netdata_configured_variables(); + default_rrd_update_every = 1; + default_rrd_memory_mode = RRD_MEMORY_MODE_RAM; + default_health_enabled = 0; + rrd_init("unittest"); + default_rrdpush_enabled = 0; if(run_all_mockup_tests()) exit(1); if(unit_test_storage()) exit(1); fprintf(stderr, "\n\nALL TESTS PASSED\n\n"); @@ -492,11 +684,11 @@ int main(int argc, char **argv) } else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) { optarg += strlen(stacksize_string); - config_set("global", "pthread stack size", optarg); + config_set(CONFIG_SECTION_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); + config_set(CONFIG_SECTION_GLOBAL, "debug flags", optarg); debug_flags = strtoull(optarg, NULL, 0); } } @@ -508,150 +700,85 @@ int main(int argc, char **argv) } } +#ifdef _SC_OPEN_MAX + // close all open file descriptors, except the standard ones + // the caller may have left open files (lxc-attach has this issue) + { + int fd; + for(fd = (int) (sysconf(_SC_OPEN_MAX) - 1); fd > 2; fd--) + if(fd_is_valid(fd)) close(fd); + } +#endif + if(!config_loaded) - load_config(NULL, 0); + config_load(NULL, 0); + // ------------------------------------------------------------------------ + // initialize netdata { - char *pmax = config_get("global", "glibc malloc arena max for plugins", "1"); + char *pmax = config_get(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for plugins", "1"); if(pmax && *pmax) setenv("MALLOC_ARENA_MAX", pmax, 1); #if defined(HAVE_C_MALLOPT) - int i = config_get_number("global", "glibc malloc arena max for netdata", 1); + i = (int)config_get_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for netdata", 1); if(i > 0) mallopt(M_ARENA_MAX, 1); #endif - 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); + get_netdata_configured_variables(); + set_global_environment(); // 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); + if(chdir(netdata_configured_config_dir) == -1) + fatal("Cannot cd to '%s'", netdata_configured_config_dir); } char *user = NULL; + { - char *flags = config_get("global", "debug flags", "0x00000000"); + // -------------------------------------------------------------------- + // get the debugging flags from the configuration file + + char *flags = config_get(CONFIG_SECTION_GLOBAL, "debug flags", "0x0000000000000000"); setenv("NETDATA_DEBUG_FLAGS", flags, 1); debug_flags = strtoull(flags, NULL, 0); - debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags); + debug(D_OPTIONS, "Debug flags set to '0x%" PRIX64 "'.", debug_flags); if(debug_flags != 0) { struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; if(setrlimit(RLIMIT_CORE, &rl) != 0) error("Cannot request unlimited core dumps for debugging... Proceeding anyway..."); -#if !(defined(__FreeBSD__) || defined(__APPLE__)) +#ifdef HAVE_SYS_PRCTL_H prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); -#endif /* __FreeBSD__ || __APPLE__*/ - } - - // -------------------------------------------------------------------- - -#ifdef MADV_MERGEABLE - 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); - get_system_HZ(); - get_system_cpus(); - get_system_pid_max(); - // -------------------------------------------------------------------- + // get log filenames and settings - 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_backup = - 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; - } + log_init(); error_log_limit_unlimited(); - // -------------------------------------------------------------------- - - rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode))); // -------------------------------------------------------------------- - + // load stream.conf { - 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); + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s/stream.conf", netdata_configured_config_dir); + appconfig_load(&stream_config, filename, 0); } - // -------------------------------------------------------------------- - - 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) { - error("Invalid history entries %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) { - error("Invalid data collection frequency (update every) %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); - } // -------------------------------------------------------------------- + // setup process signals // block signals while initializing threads. // this causes the threads to block signals. @@ -697,7 +824,9 @@ int main(int argc, char **argv) if(sigaction(SIGUSR2, &sa, NULL) == -1) error("Failed to change signal handler for SIGUSR2"); + // -------------------------------------------------------------------- + // get the required stack size of the threads of netdata i = pthread_attr_init(&attr); if(i != 0) @@ -709,30 +838,44 @@ int main(int argc, char **argv) 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); + wanted_stacksize = (size_t)config_get_number(CONFIG_SECTION_GLOBAL, "pthread stack size", (long)stacksize); + // -------------------------------------------------------------------- + // check which threads are enabled and initialize them 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(); + 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:""); + if(getuid() == 0) { + user = config_get(CONFIG_SECTION_GLOBAL, "run as user", NETDATA_USER); + } + else { + struct passwd *passwd = getpwuid(getuid()); + user = config_get(CONFIG_SECTION_GLOBAL, "run as user", (passwd && passwd->pw_name)?passwd->pw_name:""); + } // IMPORTANT: these have to run once, while single threaded web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid() web_files_gid(); + // -------------------------------------------------------------------- + // create the listening sockets - if(!check_config) + if(web_server_mode != WEB_SERVER_MODE_NONE) create_listen_sockets(); } @@ -744,20 +887,22 @@ int main(int argc, char **argv) struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; if(setrlimit(RLIMIT_CORE, &rl) != 0) error("Cannot request unlimited core dumps for debugging... Proceeding anyway..."); -#if !(defined(__FreeBSD__) || defined(__APPLE__)) +#ifdef HAVE_SYS_PRCTL_H prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); -#endif /* __FreeBSD__ || __APPLE__*/ +#endif } #endif /* NETDATA_INTERNAL_CHECKS */ + // fork, switch user, create pid file, set process priority if(become_daemon(dont_fork, user) == -1) fatal("Cannot daemonize myself."); info("netdata started on pid %d.", getpid()); + // ------------------------------------------------------------------------ - // get default pthread stack size + // set default pthread stack size - after we have forked if(stacksize < wanted_stacksize) { i = pthread_attr_setstacksize(&attr, wanted_stacksize); @@ -767,29 +912,19 @@ int main(int argc, char **argv) debug(D_SYSTEM, "Successfully set pthread stacksize to %zu bytes", wanted_stacksize); } - // ------------------------------------------------------------------------ - // initialize rrd host - - rrdhost_init(hostname); // ------------------------------------------------------------------------ - // initialize the registry + // initialize rrd, registry, health, rrdpush, etc. - registry_init(); - - // ------------------------------------------------------------------------ - // initialize health monitoring + rrd_init(netdata_configured_hostname); - health_init(); - - if(check_config) - exit(1); // ------------------------------------------------------------------------ // enable log flood protection error_log_limit_reset(); + // ------------------------------------------------------------------------ // spawn the threads @@ -814,6 +949,7 @@ int main(int argc, char **argv) info("netdata initialization completed. Enjoy real-time performance monitoring!"); + // ------------------------------------------------------------------------ // block signals while initializing threads. sigset_t sigset; diff --git a/src/main.h b/src/main.h index 49afaef12..38df0fea4 100644 --- a/src/main.h +++ b/src/main.h @@ -1,8 +1,6 @@ #ifndef NETDATA_MAIN_H #define NETDATA_MAIN_H 1 -extern volatile sig_atomic_t netdata_exit; - /** * This struct contains information about command line options. */ diff --git a/src/plugin_checks.c b/src/plugin_checks.c index fcc542e68..3a0a83bda 100644 --- a/src/plugin_checks.c +++ b/src/plugin_checks.c @@ -11,23 +11,26 @@ void *checks_main(void *ptr) { if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); - usec_t usec = 0, susec = rrd_update_every * USEC_PER_SEC, loop_usec = 0, total_susec = 0; + usec_t usec = 0, susec = localhost->rrd_update_every * USEC_PER_SEC, loop_usec = 0, total_susec = 0; struct timeval now, last, loop; 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_localhost("netdata", "check1", NULL, "netdata", NULL, "Caller gives microseconds" + , "a million !", 99999, localhost->rrd_update_every, RRDSET_TYPE_LINE); + rrddim_add(check1, "absolute", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(check1, "incremental", NULL, 1, 1, RRD_ALGORITHM_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_localhost("netdata", "check2", NULL, "netdata", NULL, "Netdata calcs microseconds" + , "a million !", 99999, localhost->rrd_update_every, RRDSET_TYPE_LINE); + rrddim_add(check2, "absolute", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(check2, "incremental", NULL, 1, 1, RRD_ALGORITHM_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_localhost("netdata", "checkdt", NULL, "netdata", NULL, "Clock difference" + , "microseconds diff", 99999, localhost->rrd_update_every, RRDSET_TYPE_LINE); + rrddim_add(check3, "caller", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(check3, "netdata", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(check3, "apps.plugin", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); now_realtime_timeval(&last); while(1) { @@ -39,8 +42,8 @@ void *checks_main(void *ptr) { 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 * USEC_PER_SEC / 2ULL)) susec = (rrd_update_every * USEC_PER_SEC) - usec; - else susec = rrd_update_every * USEC_PER_SEC / 2ULL; + if(usec < (localhost->rrd_update_every * USEC_PER_SEC / 2ULL)) susec = (localhost->rrd_update_every * USEC_PER_SEC) - usec; + else susec = localhost->rrd_update_every * USEC_PER_SEC / 2ULL; // -------------------------------------------------------------------- // Calculate loop time @@ -68,7 +71,7 @@ void *checks_main(void *ptr) { // -------------------------------------------------------------------- // check chart 3 - if(!apps_cpu) apps_cpu = rrdset_find("apps.cpu"); + if(!apps_cpu) apps_cpu = rrdset_find_localhost("apps.cpu"); if(check3->counter_done) rrdset_next_usec(check3, loop_usec); now_realtime_timeval(&loop); rrddim_set(check3, "caller", (long long) dt_usec(&loop, &check1->last_collected_time)); diff --git a/src/plugin_freebsd.c b/src/plugin_freebsd.c index bdc3599ea..31ab6e0c4 100644 --- a/src/plugin_freebsd.c +++ b/src/plugin_freebsd.c @@ -1,5 +1,62 @@ #include "common.h" +static struct freebsd_module { + const char *name; + const char *dim; + + int enabled; + + int (*func)(int update_every, usec_t dt); + usec_t duration; + + RRDDIM *rd; + +} freebsd_modules[] = { + + // system metrics + { .name = "kern.cp_time", .dim = "cp_time", .enabled = 1, .func = do_kern_cp_time }, + { .name = "vm.loadavg", .dim = "loadavg", .enabled = 1, .func = do_vm_loadavg }, + { .name = "system.ram", .dim = "system_ram", .enabled = 1, .func = do_system_ram }, + { .name = "vm.swap_info", .dim = "swap", .enabled = 1, .func = do_vm_swap_info }, + { .name = "vm.stats.vm.v_swappgs", .dim = "swap_io", .enabled = 1, .func = do_vm_stats_sys_v_swappgs }, + { .name = "vm.vmtotal", .dim = "vmtotal", .enabled = 1, .func = do_vm_vmtotal }, + { .name = "vm.stats.vm.v_forks", .dim = "forks", .enabled = 1, .func = do_vm_stats_sys_v_forks }, + { .name = "vm.stats.sys.v_swtch", .dim = "context_swtch", .enabled = 1, .func = do_vm_stats_sys_v_swtch }, + { .name = "hw.intrcnt", .dim = "hw_intr", .enabled = 1, .func = do_hw_intcnt }, + { .name = "vm.stats.sys.v_intr", .dim = "dev_intr", .enabled = 1, .func = do_vm_stats_sys_v_intr }, + { .name = "vm.stats.sys.v_soft", .dim = "soft_intr", .enabled = 1, .func = do_vm_stats_sys_v_soft }, + { .name = "net.isr", .dim = "net_isr", .enabled = 1, .func = do_net_isr }, + { .name = "kern.ipc.sem", .dim = "semaphores", .enabled = 1, .func = do_kern_ipc_sem }, + { .name = "kern.ipc.shm", .dim = "shared_memory", .enabled = 1, .func = do_kern_ipc_shm }, + { .name = "kern.ipc.msq", .dim = "message_queues", .enabled = 1, .func = do_kern_ipc_msq }, + { .name = "uptime", .dim = "uptime", .enabled = 1, .func = do_uptime }, + + // memory metrics + { .name = "vm.stats.vm.v_pgfaults", .dim = "pgfaults", .enabled = 1, .func = do_vm_stats_sys_v_pgfaults }, + + // CPU metrics + { .name = "kern.cp_times", .dim = "cp_times", .enabled = 1, .func = do_kern_cp_times }, + + // disk metrics + { .name = "kern.devstat", .dim = "kern_devstat", .enabled = 1, .func = do_kern_devstat }, + { .name = "getmntinfo", .dim = "getmntinfo", .enabled = 1, .func = do_getmntinfo }, + + // network metrics + { .name = "net.inet.tcp.states", .dim = "tcp_states", .enabled = 1, .func = do_net_inet_tcp_states }, + { .name = "net.inet.tcp.stats", .dim = "tcp_stats", .enabled = 1, .func = do_net_inet_tcp_stats }, + { .name = "net.inet.udp.stats", .dim = "udp_stats", .enabled = 1, .func = do_net_inet_udp_stats }, + { .name = "net.inet.icmp.stats", .dim = "icmp_stats", .enabled = 1, .func = do_net_inet_icmp_stats }, + { .name = "net.inet.ip.stats", .dim = "ip_stats", .enabled = 1, .func = do_net_inet_ip_stats }, + { .name = "net.inet6.ip6.stats", .dim = "ip6_stats", .enabled = 1, .func = do_net_inet6_ip6_stats }, + { .name = "net.inet6.icmp6.stats", .dim = "icmp6_stats", .enabled = 1, .func = do_net_inet6_icmp6_stats }, + + // network interfaces metrics + { .name = "getifaddrs", .dim = "getifaddrs", .enabled = 1, .func = do_getifaddrs }, + + // the terminator of this array + { .name = NULL, .dim = NULL, .enabled = 0, .func = NULL } +}; + void *freebsd_main(void *ptr) { struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; @@ -11,46 +68,80 @@ void *freebsd_main(void *ptr) { 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); - */ + int vdo_cpu_netdata = config_get_boolean("plugin:freebsd", "netdata server resources", 1); - // when ZERO, attempt to do it - int vdo_cpu_netdata = !config_get_boolean("plugin:freebsd", "netdata server resources", 1); - int vdo_freebsd_sysctl = !config_get_boolean("plugin:freebsd", "sysctl", 1); + // initialize FreeBSD plugin + if (freebsd_plugin_init()) + netdata_exit = 1; - // keep track of the time each module was called - unsigned long long sutime_freebsd_sysctl = 0ULL; + // check the enabled status for each module + int i; + for(i = 0 ; freebsd_modules[i].name ;i++) { + struct freebsd_module *pm = &freebsd_modules[i]; - usec_t step = rrd_update_every * USEC_PER_SEC; - for(;;) { - usec_t now = now_realtime_usec(); - usec_t next = now - (now % step) + step; + pm->enabled = config_get_boolean("plugin:freebsd", pm->name, pm->enabled); + pm->duration = 0ULL; + pm->rd = NULL; + } - while(now < next) { - sleep_usec(next - now); - now = now_realtime_usec(); - } + usec_t step = localhost->rrd_update_every * USEC_PER_SEC; + heartbeat_t hb; + heartbeat_init(&hb); + + for(;;) { + usec_t hb_dt = heartbeat_next(&hb, step); + usec_t duration = 0ULL; if(unlikely(netdata_exit)) break; // BEGIN -- the job to be done - if(!vdo_freebsd_sysctl) { - debug(D_PROCNETDEV_LOOP, "FREEBSD: calling do_freebsd_sysctl()."); - now = now_realtime_usec(); - vdo_freebsd_sysctl = do_freebsd_sysctl(rrd_update_every, (sutime_freebsd_sysctl > 0)?now - sutime_freebsd_sysctl:0ULL); - sutime_freebsd_sysctl = now; + for(i = 0 ; freebsd_modules[i].name ;i++) { + struct freebsd_module *pm = &freebsd_modules[i]; + if(unlikely(!pm->enabled)) continue; + + debug(D_PROCNETDEV_LOOP, "FREEBSD calling %s.", pm->name); + + pm->enabled = !pm->func(localhost->rrd_update_every, hb_dt); + pm->duration = heartbeat_dt_usec(&hb) - duration; + duration += pm->duration; + + if(unlikely(netdata_exit)) break; } - if(unlikely(netdata_exit)) break; // END -- the job is done // -------------------------------------------------------------------- - if(!vdo_cpu_netdata) { + if(vdo_cpu_netdata) { + static RRDSET *st = NULL; + + if(unlikely(!st)) { + st = rrdset_find_bytype_localhost("netdata", "plugin_freebsd_modules"); + + if(!st) { + st = rrdset_create_localhost("netdata", "plugin_freebsd_modules", NULL, "freebsd", NULL + , "NetData FreeBSD Plugin Modules Durations", "milliseconds/run", 132001 + , localhost->rrd_update_every, RRDSET_TYPE_STACKED); + + for(i = 0 ; freebsd_modules[i].name ;i++) { + struct freebsd_module *pm = &freebsd_modules[i]; + if(unlikely(!pm->enabled)) continue; + + pm->rd = rrddim_add(st, pm->dim, NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + } + } + } + else rrdset_next(st); + + for(i = 0 ; freebsd_modules[i].name ;i++) { + struct freebsd_module *pm = &freebsd_modules[i]; + if(unlikely(!pm->enabled)) continue; + + rrddim_set_by_pointer(st, pm->rd, pm->duration); + } + rrdset_done(st); + global_statistics_charts(); registry_statistics(); } @@ -62,18 +153,3 @@ void *freebsd_main(void *ptr) { pthread_exit(NULL); return NULL; } - -int getsysctl(const char *name, void *ptr, size_t len) -{ - size_t nlen = len; - - if (unlikely(sysctlbyname(name, ptr, &nlen, NULL, 0) == -1)) { - error("FREEBSD: sysctl(%s...) failed: %s", name, strerror(errno)); - return 1; - } - if (unlikely(nlen != len)) { - error("FREEBSD: sysctl(%s...) expected %lu, got %lu", name, (unsigned long)len, (unsigned long)nlen); - return 1; - } - return 0; -} diff --git a/src/plugin_freebsd.h b/src/plugin_freebsd.h index e4767a091..166c64338 100644 --- a/src/plugin_freebsd.h +++ b/src/plugin_freebsd.h @@ -3,12 +3,117 @@ #include <sys/sysctl.h> -#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var)) - void *freebsd_main(void *ptr); -int getsysctl(const char *name, void *ptr, size_t len); +extern int freebsd_plugin_init(); + +extern int do_vm_loadavg(int update_every, usec_t dt); +extern int do_vm_vmtotal(int update_every, usec_t dt); +extern int do_kern_cp_time(int update_every, usec_t dt); +extern int do_kern_cp_times(int update_every, usec_t dt); +extern int do_hw_intcnt(int update_every, usec_t dt); +extern int do_vm_stats_sys_v_intr(int update_every, usec_t dt); +extern int do_vm_stats_sys_v_soft(int update_every, usec_t dt); +extern int do_vm_stats_sys_v_swtch(int update_every, usec_t dt); +extern int do_vm_stats_sys_v_forks(int update_every, usec_t dt); +extern int do_vm_swap_info(int update_every, usec_t dt); +extern int do_system_ram(int update_every, usec_t dt); +extern int do_vm_stats_sys_v_swappgs(int update_every, usec_t dt); +extern int do_vm_stats_sys_v_pgfaults(int update_every, usec_t dt); +extern int do_kern_ipc_sem(int update_every, usec_t dt); +extern int do_kern_ipc_shm(int update_every, usec_t dt); +extern int do_kern_ipc_msq(int update_every, usec_t dt); +extern int do_uptime(int update_every, usec_t dt); +extern int do_net_isr(int update_every, usec_t dt); +extern int do_net_inet_tcp_states(int update_every, usec_t dt); +extern int do_net_inet_tcp_stats(int update_every, usec_t dt); +extern int do_net_inet_udp_stats(int update_every, usec_t dt); +extern int do_net_inet_icmp_stats(int update_every, usec_t dt); +extern int do_net_inet_ip_stats(int update_every, usec_t dt); +extern int do_net_inet6_ip6_stats(int update_every, usec_t dt); +extern int do_net_inet6_icmp6_stats(int update_every, usec_t dt); +extern int do_getifaddrs(int update_every, usec_t dt); +extern int do_getmntinfo(int update_every, usec_t dt); +extern int do_kern_devstat(int update_every, usec_t dt); + +#define GETSYSCTL_MIB(name, mib) getsysctl_mib(name, mib, sizeof(mib)/sizeof(int)) + +static inline int getsysctl_mib(const char *name, int *mib, size_t len) +{ + size_t nlen = len; + + if (unlikely(sysctlnametomib(name, mib, &nlen) == -1)) { + error("FREEBSD: sysctl(%s...) failed: %s", name, strerror(errno)); + return 1; + } + if (unlikely(nlen != len)) { + error("FREEBSD: sysctl(%s...) expected %lu, got %lu", name, (unsigned long)len, (unsigned long)nlen); + return 1; + } + return 0; +} + +#define GETSYSCTL_SIMPLE(name, mib, var) getsysctl_simple(name, mib, sizeof(mib)/sizeof(int), &(var), sizeof(var)) +#define GETSYSCTL_WSIZE(name, mib, var, size) getsysctl_simple(name, mib, sizeof(mib)/sizeof(int), var, size) + +static inline int getsysctl_simple(const char *name, int *mib, size_t miblen, void *ptr, size_t len) +{ + size_t nlen = len; + + if (unlikely(!mib[0])) + if (unlikely(getsysctl_mib(name, mib, miblen))) + return 1; + + if (unlikely(sysctl(mib, miblen, ptr, &nlen, NULL, 0) == -1)) { + error("FREEBSD: sysctl(%s...) failed: %s", name, strerror(errno)); + return 1; + } + if (unlikely(nlen != len)) { + error("FREEBSD: sysctl(%s...) expected %lu, got %lu", name, (unsigned long)len, (unsigned long)nlen); + return 1; + } + + return 0; +} + +#define GETSYSCTL_SIZE(name, mib, size) getsysctl(name, mib, sizeof(mib)/sizeof(int), NULL, &(size)) +#define GETSYSCTL(name, mib, var, size) getsysctl(name, mib, sizeof(mib)/sizeof(int), &(var), &(size)) + +static inline int getsysctl(const char *name, int *mib, size_t miblen, void *ptr, size_t *len) +{ + size_t nlen = *len; + + if (unlikely(!mib[0])) + if (unlikely(getsysctl_mib(name, mib, miblen))) + return 1; + + if (unlikely(sysctl(mib, miblen, ptr, len, NULL, 0) == -1)) { + error("FREEBSD: sysctl(%s...) failed: %s", name, strerror(errno)); + return 1; + } + if (unlikely(ptr != NULL && nlen != *len)) { + error("FREEBSD: sysctl(%s...) expected %lu, got %lu", name, (unsigned long)*len, (unsigned long)nlen); + return 1; + } + + return 0; +} + +#define GETSYSCTL_BY_NAME(name, var) getsysctl_by_name(name, &(var), sizeof(var)) + +static inline int getsysctl_by_name(const char *name, void *ptr, size_t len) +{ + size_t nlen = len; -extern int do_freebsd_sysctl(int update_every, usec_t dt); + if (unlikely(sysctlbyname(name, ptr, &nlen, NULL, 0) == -1)) { + error("FREEBSD: sysctl(%s...) failed: %s", name, strerror(errno)); + return 1; + } + if (unlikely(nlen != len)) { + error("FREEBSD: sysctl(%s...) expected %lu, got %lu", name, (unsigned long)len, (unsigned long)nlen); + return 1; + } + return 0; +} #endif /* NETDATA_PLUGIN_FREEBSD_H */ diff --git a/src/plugin_idlejitter.c b/src/plugin_idlejitter.c index 7d4a4c189..2ed78160c 100644 --- a/src/plugin_idlejitter.c +++ b/src/plugin_idlejitter.c @@ -19,10 +19,11 @@ void *cpuidlejitter_main(void *ptr) { sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS; } - RRDSET *st = rrdset_find("system.idlejitter"); + RRDSET *st = rrdset_find_localhost("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); + st = rrdset_create_localhost("system", "idlejitter", NULL, "processes", NULL, "CPU Idle Jitter" + , "microseconds lost/s", 9999, localhost->rrd_update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "jitter", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } struct timeval before, after; @@ -30,11 +31,13 @@ void *cpuidlejitter_main(void *ptr) { for(counter = 0; 1 ;counter++) { usec_t usec = 0, susec = 0; - while(susec < (rrd_update_every * USEC_PER_SEC)) { + if(netdata_exit) break; - now_realtime_timeval(&before); + while(susec < (localhost->rrd_update_every * USEC_PER_SEC)) { + + now_monotonic_timeval(&before); sleep_usec(sleep_ms * 1000); - now_realtime_timeval(&after); + now_monotonic_timeval(&after); // calculate the time it took for a full loop usec = dt_usec(&after, &before); diff --git a/src/plugin_macos.c b/src/plugin_macos.c index 3955c1414..4e84a084d 100644 --- a/src/plugin_macos.c +++ b/src/plugin_macos.c @@ -11,12 +11,6 @@ void *macos_main(void *ptr) { 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_cpu_netdata = !config_get_boolean("plugin:macos", "netdata server resources", 1); int vdo_macos_sysctl = !config_get_boolean("plugin:macos", "sysctl", 1); @@ -28,15 +22,11 @@ void *macos_main(void *ptr) { unsigned long long sutime_macos_mach_smi = 0ULL; unsigned long long sutime_macos_iokit = 0ULL; - usec_t step = rrd_update_every * USEC_PER_SEC; + usec_t step = localhost->rrd_update_every * USEC_PER_SEC; + heartbeat_t hb; + heartbeat_init(&hb); for(;;) { - usec_t now = now_realtime_usec(); - usec_t next = now - (now % step) + step; - - while(now < next) { - sleep_usec(next - now); - now = now_realtime_usec(); - } + usec_t hb_dt = heartbeat_next(&hb, step); if(unlikely(netdata_exit)) break; @@ -44,25 +34,19 @@ void *macos_main(void *ptr) { if(!vdo_macos_sysctl) { debug(D_PROCNETDEV_LOOP, "MACOS: calling do_macos_sysctl()."); - now = now_realtime_usec(); - vdo_macos_sysctl = do_macos_sysctl(rrd_update_every, (sutime_macos_sysctl > 0)?now - sutime_macos_sysctl:0ULL); - sutime_macos_sysctl = now; + vdo_macos_sysctl = do_macos_sysctl(localhost->rrd_update_every, hb_dt); } if(unlikely(netdata_exit)) break; if(!vdo_macos_mach_smi) { debug(D_PROCNETDEV_LOOP, "MACOS: calling do_macos_mach_smi()."); - now = now_realtime_usec(); - vdo_macos_mach_smi = do_macos_mach_smi(rrd_update_every, (sutime_macos_mach_smi > 0)?now - sutime_macos_mach_smi:0ULL); - sutime_macos_mach_smi = now; + vdo_macos_mach_smi = do_macos_mach_smi(localhost->rrd_update_every, hb_dt); } if(unlikely(netdata_exit)) break; if(!vdo_macos_iokit) { debug(D_PROCNETDEV_LOOP, "MACOS: calling do_macos_iokit()."); - now = now_realtime_usec(); - vdo_macos_iokit = do_macos_iokit(rrd_update_every, (sutime_macos_iokit > 0)?now - sutime_macos_iokit:0ULL); - sutime_macos_iokit = now; + vdo_macos_iokit = do_macos_iokit(localhost->rrd_update_every, hb_dt); } if(unlikely(netdata_exit)) break; diff --git a/src/plugin_macos.h b/src/plugin_macos.h index a21e5601d..6ccf3e861 100644 --- a/src/plugin_macos.h +++ b/src/plugin_macos.h @@ -3,9 +3,9 @@ void *macos_main(void *ptr); -#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var)) +#define GETSYSCTL_BY_NAME(name, var) getsysctl_by_name(name, &(var), sizeof(var)) -extern int getsysctl(const char *name, void *ptr, size_t len); +extern int getsysctl_by_name(const char *name, void *ptr, size_t len); extern int do_macos_sysctl(int update_every, usec_t dt); extern int do_macos_mach_smi(int update_every, usec_t dt); diff --git a/src/plugin_nfacct.c b/src/plugin_nfacct.c index 7aae33c0c..4c691be05 100644 --- a/src/plugin_nfacct.c +++ b/src/plugin_nfacct.c @@ -1,196 +1,809 @@ #include "common.h" #ifdef INTERNAL_PLUGIN_NFACCT + +#ifdef HAVE_LIBMNL #include <libmnl/libmnl.h> -#include <libnetfilter_acct/libnetfilter_acct.h> -struct mynfacct { - const char *name; - uint64_t pkts; - uint64_t bytes; - struct nfacct *nfacct; +static inline size_t mnl_buffer_size() { + long s = MNL_SOCKET_BUFFER_SIZE; + if(s <= 0) return 8192; + return (size_t)s; +} + +// ---------------------------------------------------------------------------- +// DO_NFSTAT - collect netfilter connection tracker statistics via netlink +// example: https://github.com/formorer/pkg-conntrack-tools/blob/master/src/conntrack.c + +#ifdef HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H +#define DO_NFSTAT 1 + +#define RRD_TYPE_NET_STAT_NETFILTER "netfilter" +#define RRD_TYPE_NET_STAT_CONNTRACK "netlink" // FIXME: should be "conntrack" when merged with the /proc plugin + +#include <linux/netfilter/nfnetlink_conntrack.h> + +static struct { + int update_every; + char *buf; + size_t buf_size; + struct mnl_socket *mnl; + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + unsigned int seq; + uint32_t portid; + + struct nlattr *tb[CTA_STATS_MAX+1]; + const char *attr2name[CTA_STATS_MAX+1]; + kernel_uint_t metrics[CTA_STATS_MAX+1]; + + struct nlattr *tb_exp[CTA_STATS_EXP_MAX+1]; + const char *attr2name_exp[CTA_STATS_EXP_MAX+1]; + kernel_uint_t metrics_exp[CTA_STATS_EXP_MAX+1]; +} nfstat_root = { + .update_every = 1, + .buf = NULL, + .buf_size = 0, + .mnl = NULL, + .nlh = NULL, + .nfh = NULL, + .seq = 0, + .portid = 0, + .tb = {}, + .attr2name = { + [CTA_STATS_SEARCHED] = "searched", + [CTA_STATS_FOUND] = "found", + [CTA_STATS_NEW] = "new", + [CTA_STATS_INVALID] = "invalid", + [CTA_STATS_IGNORE] = "ignore", + [CTA_STATS_DELETE] = "delete", + [CTA_STATS_DELETE_LIST] = "delete_list", + [CTA_STATS_INSERT] = "insert", + [CTA_STATS_INSERT_FAILED] = "insert_failed", + [CTA_STATS_DROP] = "drop", + [CTA_STATS_EARLY_DROP] = "early_drop", + [CTA_STATS_ERROR] = "icmp_error", + [CTA_STATS_SEARCH_RESTART] = "search_restart", + }, + .metrics = {}, + .tb_exp = {}, + .attr2name_exp = { + [CTA_STATS_EXP_NEW] = "new", + [CTA_STATS_EXP_CREATE] = "created", + [CTA_STATS_EXP_DELETE] = "deleted", + }, + .metrics_exp = {} }; -struct nfacct_list { - int size; - int len; - struct mynfacct data[]; -} *nfacct_list = NULL; -static int nfacct_callback(const struct nlmsghdr *nlh, void *data) { - if(data) {}; +static int nfstat_init(int update_every) { + nfstat_root.update_every = update_every; - 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++; + nfstat_root.buf_size = mnl_buffer_size(); + nfstat_root.buf = mallocz(nfstat_root.buf_size); - info("nfacct.plugin: increasing nfacct_list to size %d", size); + nfstat_root.mnl = mnl_socket_open(NETLINK_NETFILTER); + if(!nfstat_root.mnl) { + error("NFSTAT: mnl_socket_open() failed"); + return 1; + } - nfacct_list = reallocz(nfacct_list, sizeof(struct nfacct_list) + (sizeof(struct mynfacct) * size)); + nfstat_root.seq = (unsigned int)now_realtime_sec() - 1; - 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; - } + if(mnl_socket_bind(nfstat_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) { + error("NFSTAT: mnl_socket_bind() failed"); + return 1; + } + nfstat_root.portid = mnl_socket_get_portid(nfstat_root.mnl); + + return 0; +} - nfacct_list->size = size; - nfacct_list->len = len; +static void nfstat_cleanup() { + if(nfstat_root.mnl) { + mnl_socket_close(nfstat_root.mnl); + nfstat_root.mnl = NULL; } - if(nfacct_nlmsg_parse_payload(nlh, nfacct_list->data[nfacct_list->len].nfacct) < 0) { - error("nfacct.plugin: nfacct_nlmsg_parse_payload() failed."); + freez(nfstat_root.buf); + nfstat_root.buf = NULL; + nfstat_root.buf_size = 0; +} + +static struct nlmsghdr * nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type, uint8_t family, uint32_t seq) { + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (subsys << 8) | type; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + nlh->nlmsg_seq = seq; + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = family; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + return nlh; +} + +static int nfct_stats_attr_cb(const struct nlattr *attr, void *data) { + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0) return MNL_CB_OK; + + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + error("NFSTAT: mnl_attr_validate() failed"); + return MNL_CB_ERROR; } - 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); + tb[type] = attr; + return MNL_CB_OK; +} + +static int nfstat_callback(const struct nlmsghdr *nlh, void *data) { + (void)data; + + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_attr_cb, nfstat_root.tb); + + // printf("cpu=%-4u\t", ntohs(nfg->res_id)); + + int i; + // add the metrics of this CPU into the metrics + for (i = 0; i < CTA_STATS_MAX+1; i++) { + if (nfstat_root.tb[i]) { + // printf("%s=%u ", nfstat_root.attr2name[i], ntohl(mnl_attr_get_u32(nfstat_root.tb[i]))); + nfstat_root.metrics[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb[i])); + } + } + // printf("\n"); - nfacct_list->len++; return MNL_CB_OK; } -void *nfacct_main(void *ptr) { - struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; +static int nfstat_collect_conntrack() { + // zero all metrics - we will sum the metrics of all CPUs later + int i; + for (i = 0; i < CTA_STATS_MAX+1; i++) + nfstat_root.metrics[i] = 0; - info("NFACCT thread created with task id %d", gettid()); + // prepare the request + nfstat_root.nlh = nfct_mnl_nlmsghdr_put(nfstat_root.buf, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS_CPU, AF_UNSPEC, nfstat_root.seq); - if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) - error("nfacct.plugin: Cannot set pthread cancel type to DEFERRED."); + // send the request + if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) { + error("NFSTAT: mnl_socket_sendto() failed"); + return 1; + } - if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) - error("nfacct.plugin: Cannot set pthread cancel state to ENABLE."); + // get the reply + ssize_t ret; + while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) { + if(mnl_cb_run( + nfstat_root.buf + , (size_t)ret + , nfstat_root.nlh->nlmsg_seq + , nfstat_root.portid + , nfstat_callback + , NULL + ) <= MNL_CB_STOP) + break; + } + + // verify we run without issues + if (ret == -1) { + error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root."); + return 1; + } - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct mnl_socket *nl = NULL; - struct nlmsghdr *nlh = NULL; - unsigned int seq = 0, portid = 0; + return 0; +} - seq = now_realtime_sec() - 1; +static int nfexp_stats_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); - nl = mnl_socket_open(NETLINK_NETFILTER); - if(!nl) { - error("nfacct.plugin: mnl_socket_open() failed"); - goto cleanup; + if (mnl_attr_type_valid(attr, CTA_STATS_EXP_MAX) < 0) + return MNL_CB_OK; + + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + error("NFSTAT EXP: mnl_attr_validate() failed"); + return MNL_CB_ERROR; } - if(mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - error("nfacct.plugin: mnl_socket_bind() failed"); - goto cleanup; + tb[type] = attr; + return MNL_CB_OK; +} + +static int nfstat_callback_exp(const struct nlmsghdr *nlh, void *data) { + (void)data; + + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*nfg), nfexp_stats_attr_cb, nfstat_root.tb_exp); + + int i; + for (i = 0; i < CTA_STATS_EXP_MAX+1; i++) { + if (nfstat_root.tb_exp[i]) { + nfstat_root.metrics_exp[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb_exp[i])); + } } - portid = mnl_socket_get_portid(nl); - // ------------------------------------------------------------------------ + return MNL_CB_OK; +} - struct timeval last, now; - usec_t usec = 0, susec = 0; - RRDSET *st = NULL; +static int nfstat_collect_conntrack_expectations() { + // zero all metrics - we will sum the metrics of all CPUs later + int i; + for (i = 0; i < CTA_STATS_EXP_MAX+1; i++) + nfstat_root.metrics_exp[i] = 0; - now_realtime_timeval(&last); + // prepare the request + nfstat_root.nlh = nfct_mnl_nlmsghdr_put(nfstat_root.buf, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET_STATS_CPU, AF_UNSPEC, nfstat_root.seq); - // ------------------------------------------------------------------------ + // send the request + if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) { + error("NFSTAT: mnl_socket_sendto() failed"); + return 1; + } - while(1) { - if(unlikely(netdata_exit)) break; + // get the reply + ssize_t ret; + while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) { + if(mnl_cb_run( + nfstat_root.buf + , (size_t)ret + , nfstat_root.nlh->nlmsg_seq + , nfstat_root.portid + , nfstat_callback_exp + , NULL + ) <= MNL_CB_STOP) + break; + } - seq++; + // verify we run without issues + if (ret == -1) { + error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root."); + return 1; + } + + return 0; +} + +static int nfstat_collect() { + nfstat_root.seq++; + + if(nfstat_collect_conntrack()) + return 1; + + if(nfstat_collect_conntrack_expectations()) + return 1; + + return 0; +} - nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, seq); - if(!nlh) { - error("nfacct.plugin: nfacct_nlmsg_build_hdr() failed"); - goto cleanup; +static void nfstat_send_metrics() { + + { + static RRDSET *st_new = NULL; + static RRDDIM *rd_new = NULL, *rd_ignore = NULL, *rd_invalid = NULL; + + if(!st_new) { + st_new = rrdset_create_localhost( + RRD_TYPE_NET_STAT_NETFILTER + , RRD_TYPE_NET_STAT_CONNTRACK "_new" + , NULL + , RRD_TYPE_NET_STAT_CONNTRACK + , NULL + , "Connection Tracker New Connections" + , "connections/s" + , 3001 + , nfstat_root.update_every + , RRDSET_TYPE_LINE + ); + + rd_new = rrddim_add(st_new, nfstat_root.attr2name[CTA_STATS_NEW], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_ignore = rrddim_add(st_new, nfstat_root.attr2name[CTA_STATS_IGNORE], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_invalid = rrddim_add(st_new, nfstat_root.attr2name[CTA_STATS_INVALID], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } + else + rrdset_next(st_new); - if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - error("nfacct.plugin: mnl_socket_send"); - goto cleanup; + rrddim_set_by_pointer(st_new, rd_new, (collected_number) nfstat_root.metrics[CTA_STATS_NEW]); + rrddim_set_by_pointer(st_new, rd_ignore, (collected_number) nfstat_root.metrics[CTA_STATS_IGNORE]); + rrddim_set_by_pointer(st_new, rd_invalid, (collected_number) nfstat_root.metrics[CTA_STATS_INVALID]); + + rrdset_done(st_new); + } + + // ---------------------------------------------------------------- + + { + static RRDSET *st_changes = NULL; + static RRDDIM *rd_inserted = NULL, *rd_deleted = NULL, *rd_delete_list = NULL; + + if(!st_changes) { + st_changes = rrdset_create_localhost( + RRD_TYPE_NET_STAT_NETFILTER + , RRD_TYPE_NET_STAT_CONNTRACK "_changes" + , NULL + , RRD_TYPE_NET_STAT_CONNTRACK + , NULL + , "Connection Tracker Changes" + , "changes/s" + , 3002 + , nfstat_root.update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st_changes, RRDSET_FLAG_DETAIL); + + rd_inserted = rrddim_add(st_changes, nfstat_root.attr2name[CTA_STATS_INSERT], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_deleted = rrddim_add(st_changes, nfstat_root.attr2name[CTA_STATS_DELETE], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_delete_list = rrddim_add(st_changes, nfstat_root.attr2name[CTA_STATS_DELETE_LIST], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } + else + rrdset_next(st_changes); - if(nfacct_list) nfacct_list->len = 0; + rrddim_set_by_pointer(st_changes, rd_inserted, (collected_number) nfstat_root.metrics[CTA_STATS_INSERT]); + rrddim_set_by_pointer(st_changes, rd_deleted, (collected_number) nfstat_root.metrics[CTA_STATS_DELETE]); + rrddim_set_by_pointer(st_changes, rd_delete_list, (collected_number) nfstat_root.metrics[CTA_STATS_DELETE_LIST]); - 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; + rrdset_done(st_changes); + } + + // ---------------------------------------------------------------- + + { + static RRDSET *st_search = NULL; + static RRDDIM *rd_searched = NULL, *rd_restarted = NULL, *rd_found = NULL; + + if(!st_search) { + st_search = rrdset_create_localhost( + RRD_TYPE_NET_STAT_NETFILTER + , RRD_TYPE_NET_STAT_CONNTRACK "_search" + , NULL + , RRD_TYPE_NET_STAT_CONNTRACK + , NULL + , "Connection Tracker Searches" + , "searches/s" + , 3010 + , nfstat_root.update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st_search, RRDSET_FLAG_DETAIL); + + rd_searched = rrddim_add(st_search, nfstat_root.attr2name[CTA_STATS_SEARCHED], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_restarted = rrddim_add(st_search, nfstat_root.attr2name[CTA_STATS_SEARCH_RESTART], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_found = rrddim_add(st_search, nfstat_root.attr2name[CTA_STATS_FOUND], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } + else + rrdset_next(st_search); + + rrddim_set_by_pointer(st_search, rd_searched, (collected_number) nfstat_root.metrics[CTA_STATS_SEARCHED]); + rrddim_set_by_pointer(st_search, rd_restarted, (collected_number) nfstat_root.metrics[CTA_STATS_SEARCH_RESTART]); + rrddim_set_by_pointer(st_search, rd_found, (collected_number) nfstat_root.metrics[CTA_STATS_FOUND]); - if (ret == -1) { - error("nfacct.plugin: error communicating with kernel."); - goto cleanup; + rrdset_done(st_search); + } + + // ---------------------------------------------------------------- + + { + static RRDSET *st_errors = NULL; + static RRDDIM *rd_error = NULL, *rd_insert_failed = NULL, *rd_drop = NULL, *rd_early_drop = NULL; + + if(!st_errors) { + st_errors = rrdset_create_localhost( + RRD_TYPE_NET_STAT_NETFILTER + , RRD_TYPE_NET_STAT_CONNTRACK "_errors" + , NULL + , RRD_TYPE_NET_STAT_CONNTRACK + , NULL + , "Connection Tracker Errors" + , "events/s" + , 3005 + , nfstat_root.update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st_errors, RRDSET_FLAG_DETAIL); + + rd_error = rrddim_add(st_errors, nfstat_root.attr2name[CTA_STATS_ERROR], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_insert_failed = rrddim_add(st_errors, nfstat_root.attr2name[CTA_STATS_INSERT_FAILED], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_drop = rrddim_add(st_errors, nfstat_root.attr2name[CTA_STATS_DROP], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_early_drop = rrddim_add(st_errors, nfstat_root.attr2name[CTA_STATS_EARLY_DROP], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } + else + rrdset_next(st_errors); - // -------------------------------------------------------------------- + rrddim_set_by_pointer(st_errors, rd_error, (collected_number) nfstat_root.metrics[CTA_STATS_ERROR]); + rrddim_set_by_pointer(st_errors, rd_insert_failed, (collected_number) nfstat_root.metrics[CTA_STATS_INSERT_FAILED]); + rrddim_set_by_pointer(st_errors, rd_drop, (collected_number) nfstat_root.metrics[CTA_STATS_DROP]); + rrddim_set_by_pointer(st_errors, rd_early_drop, (collected_number) nfstat_root.metrics[CTA_STATS_EARLY_DROP]); - now_realtime_timeval(&now); - usec = dt_usec(&now, &last) - susec; - debug(D_NFACCT_LOOP, "nfacct.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).", usec + susec, usec, susec); + rrdset_done(st_errors); + } - if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec; - else susec = rrd_update_every * 1000000ULL / 2ULL; + // ---------------------------------------------------------------- + + { + static RRDSET *st_expect = NULL; + static RRDDIM *rd_new = NULL, *rd_created = NULL, *rd_deleted = NULL; + + if(!st_expect) { + st_expect = rrdset_create_localhost( + RRD_TYPE_NET_STAT_NETFILTER + , RRD_TYPE_NET_STAT_CONNTRACK "_expect" + , NULL + , RRD_TYPE_NET_STAT_CONNTRACK + , NULL + , "Connection Tracker Expectations" + , "expectations/s" + , 3003 + , nfstat_root.update_every + , RRDSET_TYPE_LINE + ); + rrdset_flag_set(st_expect, RRDSET_FLAG_DETAIL); + + rd_created = rrddim_add(st_expect, nfstat_root.attr2name_exp[CTA_STATS_EXP_CREATE], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_deleted = rrddim_add(st_expect, nfstat_root.attr2name_exp[CTA_STATS_EXP_DELETE], NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_new = rrddim_add(st_expect, nfstat_root.attr2name_exp[CTA_STATS_EXP_NEW], NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else + rrdset_next(st_expect); + rrddim_set_by_pointer(st_expect, rd_created, (collected_number) nfstat_root.metrics_exp[CTA_STATS_EXP_CREATE]); + rrddim_set_by_pointer(st_expect, rd_deleted, (collected_number) nfstat_root.metrics_exp[CTA_STATS_EXP_DELETE]); + rrddim_set_by_pointer(st_expect, rd_new, (collected_number) nfstat_root.metrics_exp[CTA_STATS_EXP_NEW]); - // -------------------------------------------------------------------- + rrdset_done(st_expect); + } + +} + +#endif // HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H - if(nfacct_list && nfacct_list->len) { - int i; - st = rrdset_find_bytype("netfilter", "nfacct_packets"); - if(!st) { - st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 3206, rrd_update_every, RRDSET_TYPE_STACKED); +// ---------------------------------------------------------------------------- +// DO_NFACCT - collect netfilter accounting statistics via netlink - 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); +#ifdef HAVE_LIBNETFILTER_ACCT +#define DO_NFACCT 1 + +#include <libnetfilter_acct/libnetfilter_acct.h> - for(i = 0; i < nfacct_list->len ; i++) { - RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name); +struct nfacct_data { + char *name; + uint32_t hash; - 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); - } + uint64_t pkts; + uint64_t bytes; - rrdset_done(st); + RRDDIM *rd_bytes; + RRDDIM *rd_packets; - // ---------------------------------------------------------------- + int updated; - st = rrdset_find_bytype("netfilter", "nfacct_bytes"); - if(!st) { - st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 3207, rrd_update_every, RRDSET_TYPE_STACKED); + struct nfacct_data *next; +}; - 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); +static struct { + int update_every; + char *buf; + size_t buf_size; + struct mnl_socket *mnl; + struct nlmsghdr *nlh; + unsigned int seq; + uint32_t portid; + struct nfacct *nfacct_buffer; + struct nfacct_data *nfacct_metrics; +} nfacct_root = { + .update_every = 1, + .buf = NULL, + .buf_size = 0, + .mnl = NULL, + .nlh = NULL, + .seq = 0, + .portid = 0, + .nfacct_buffer = NULL, + .nfacct_metrics = NULL +}; - for(i = 0; i < nfacct_list->len ; i++) { - RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name); +static inline struct nfacct_data *nfacct_data_get(const char *name, uint32_t hash) { + struct nfacct_data *d = NULL, *last = NULL; + for(d = nfacct_root.nfacct_metrics; d ; last = d, d = d->next) { + if(unlikely(d->hash == hash && !strcmp(d->name, name))) + return d; + } - 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); - } + d = callocz(1, sizeof(struct nfacct_data)); + d->name = strdupz(name); + d->hash = hash; - rrdset_done(st); + if(!last) { + d->next = nfacct_root.nfacct_metrics; + nfacct_root.nfacct_metrics = d; + } + else { + d->next = last->next; + last->next = d; + } + + return d; +} + +static int nfacct_init(int update_every) { + nfacct_root.update_every = update_every; + + nfacct_root.buf_size = mnl_buffer_size(); + nfacct_root.buf = mallocz(nfacct_root.buf_size); + + nfacct_root.nfacct_buffer = nfacct_alloc(); + if(!nfacct_root.nfacct_buffer) { + error("nfacct.plugin: nfacct_alloc() failed."); + return 0; + } + + nfacct_root.seq = (unsigned int)now_realtime_sec() - 1; + + nfacct_root.mnl = mnl_socket_open(NETLINK_NETFILTER); + if(!nfacct_root.mnl) { + error("nfacct.plugin: mnl_socket_open() failed"); + return 1; + } + + if(mnl_socket_bind(nfacct_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) { + error("nfacct.plugin: mnl_socket_bind() failed"); + return 1; + } + nfacct_root.portid = mnl_socket_get_portid(nfacct_root.mnl); + + return 0; +} + +static void nfacct_cleanup() { + if(nfacct_root.mnl) { + mnl_socket_close(nfacct_root.mnl); + nfacct_root.mnl = NULL; + } + + if(nfacct_root.nfacct_buffer) { + nfacct_free(nfacct_root.nfacct_buffer); + nfacct_root.nfacct_buffer = NULL; + } + + freez(nfacct_root.buf); + nfacct_root.buf = NULL; + nfacct_root.buf_size = 0; + + // FIXME: cleanup the metrics linked list +} + +static int nfacct_callback(const struct nlmsghdr *nlh, void *data) { + (void)data; + + if(nfacct_nlmsg_parse_payload(nlh, nfacct_root.nfacct_buffer) < 0) { + error("NFACCT: nfacct_nlmsg_parse_payload() failed."); + return MNL_CB_OK; + } + + const char *name = nfacct_attr_get_str(nfacct_root.nfacct_buffer, NFACCT_ATTR_NAME); + uint32_t hash = simple_hash(name); + + struct nfacct_data *d = nfacct_data_get(name, hash); + + d->pkts = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_PKTS); + d->bytes = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_BYTES); + d->updated = 1; + + return MNL_CB_OK; +} + +static int nfacct_collect() { + // mark all old metrics as not-updated + struct nfacct_data *d; + for(d = nfacct_root.nfacct_metrics; d ; d = d->next) + d->updated = 0; + + // prepare the request + nfacct_root.seq++; + nfacct_root.nlh = nfacct_nlmsg_build_hdr(nfacct_root.buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, (uint32_t)nfacct_root.seq); + if(!nfacct_root.nlh) { + error("NFACCT: nfacct_nlmsg_build_hdr() failed"); + return 1; + } + + // send the request + if(mnl_socket_sendto(nfacct_root.mnl, nfacct_root.nlh, nfacct_root.nlh->nlmsg_len) < 0) { + error("NFACCT: mnl_socket_sendto() failed"); + return 1; + } + + // get the reply + ssize_t ret; + while((ret = mnl_socket_recvfrom(nfacct_root.mnl, nfacct_root.buf, nfacct_root.buf_size)) > 0) { + if(mnl_cb_run( + nfacct_root.buf + , (size_t)ret + , nfacct_root.seq + , nfacct_root.portid + , nfacct_callback + , NULL + ) <= 0) + break; + } + + // verify we run without issues + if (ret == -1) { + error("NFACCT: error communicating with kernel. This plugin can only work when netdata runs as root."); + return 1; + } + + return 0; +} + +static void nfacct_send_metrics() { + static RRDSET *st_bytes = NULL, *st_packets = NULL; + + if(!nfacct_root.nfacct_metrics) return; + struct nfacct_data *d; + + if(!st_packets) { + st_packets = rrdset_create_localhost( + "netfilter" + , "nfacct_packets" + , NULL + , "nfacct" + , NULL + , "Netfilter Accounting Packets" + , "packets/s" + , 3206 + , nfacct_root.update_every + , RRDSET_TYPE_STACKED + ); + } + else rrdset_next(st_packets); + + for(d = nfacct_root.nfacct_metrics; d ; d = d->next) { + if(likely(d->updated)) { + if(unlikely(!d->rd_packets)) + d->rd_packets = rrddim_add( + st_packets + , d->name + , NULL + , 1 + , nfacct_root.update_every + , RRD_ALGORITHM_INCREMENTAL + ); + + rrddim_set_by_pointer( + st_packets + , d->rd_packets + , (collected_number)d->pkts + ); } + } + + rrdset_done(st_packets); + + // ---------------------------------------------------------------- + + st_bytes = rrdset_find_bytype_localhost("netfilter", "nfacct_bytes"); + if(!st_bytes) { + st_bytes = rrdset_create_localhost( + "netfilter" + , "nfacct_bytes" + , NULL + , "nfacct" + , NULL + , "Netfilter Accounting Bandwidth" + , "kilobytes/s" + , 3207 + , nfacct_root.update_every + , RRDSET_TYPE_STACKED + ); + } + else rrdset_next(st_bytes); + + for(d = nfacct_root.nfacct_metrics; d ; d = d->next) { + if(likely(d->updated)) { + if(unlikely(!d->rd_bytes)) + d->rd_bytes = rrddim_add( + st_bytes + , d->name + , NULL + , 1 + , 1000 * nfacct_root.update_every + , RRD_ALGORITHM_INCREMENTAL + ); + + rrddim_set_by_pointer( + st_bytes + , d->rd_bytes + , (collected_number)d->bytes + ); + } + } + + rrdset_done(st_bytes); +} + +#endif // HAVE_LIBNETFILTER_ACCT +#endif // HAVE_LIBMNL + +// ---------------------------------------------------------------------------- + +void *nfacct_main(void *ptr) { + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr; + + info("NETFILTER thread created with task id %d", gettid()); + + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("NETFILTER: Cannot set pthread cancel type to DEFERRED."); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("NETFILTER: Cannot set pthread cancel state to ENABLE."); - // -------------------------------------------------------------------- - usleep(susec); + int update_every = (int)config_get_number("plugin:netfilter", "update every", localhost->rrd_update_every); + if(update_every < localhost->rrd_update_every) + update_every = localhost->rrd_update_every; - // copy current to last - memmove(&last, &now, sizeof(struct timeval)); +#ifdef DO_NFACCT + int nfacct = !nfacct_init(update_every); +#endif + +#ifdef DO_NFSTAT + int nfstat = !nfstat_init(update_every); +#endif + + // ------------------------------------------------------------------------ + + usec_t step = update_every * USEC_PER_SEC; + heartbeat_t hb; + heartbeat_init(&hb); + for(;;) { + heartbeat_dt_usec(&hb); + heartbeat_next(&hb, step); + + if(unlikely(netdata_exit)) break; + +#ifdef DO_NFACCT + if(likely(nfacct)) { + nfacct = !nfacct_collect(); + + if(likely(nfacct)) + nfacct_send_metrics(); + } +#endif + +#ifdef DO_NFSTAT + if(likely(nfstat)) { + nfstat = !nfstat_collect(); + + if(likely(nfstat)) + nfstat_send_metrics(); + } +#endif } -cleanup: - info("NFACCT thread exiting"); + info("NETFILTER thread exiting"); - if(nl) mnl_socket_close(nl); +#ifdef DO_NFACCT + nfacct_cleanup(); +#endif + +#ifdef DO_NFSTAT + nfstat_cleanup(); +#endif static_thread->enabled = 0; pthread_exit(NULL); return NULL; } -#endif + +#endif // INTERNAL_PLUGIN_NFACCT diff --git a/src/plugin_proc.c b/src/plugin_proc.c index 9b66b7c28..2ca77491d 100644 --- a/src/plugin_proc.c +++ b/src/plugin_proc.c @@ -7,7 +7,6 @@ static struct proc_module { int enabled; int (*func)(int update_every, usec_t dt); - usec_t last_run_usec; usec_t duration; RRDDIM *rd; @@ -76,20 +75,17 @@ void *proc_main(void *ptr) { struct proc_module *pm = &proc_modules[i]; pm->enabled = config_get_boolean("plugin:proc", pm->name, 1); - pm->last_run_usec = 0ULL; pm->duration = 0ULL; pm->rd = NULL; } - usec_t step = rrd_update_every * USEC_PER_SEC; - for(;;) { - usec_t now = now_monotonic_usec(); - usec_t next = now - (now % step) + step; + usec_t step = localhost->rrd_update_every * USEC_PER_SEC; + heartbeat_t hb; + heartbeat_init(&hb); - while(now < next) { - sleep_usec(next - now); - now = now_monotonic_usec(); - } + for(;;) { + usec_t hb_dt = heartbeat_next(&hb, step); + usec_t duration = 0ULL; if(unlikely(netdata_exit)) break; @@ -101,11 +97,9 @@ void *proc_main(void *ptr) { debug(D_PROCNETDEV_LOOP, "PROC calling %s.", pm->name); - pm->enabled = !pm->func(rrd_update_every, (pm->last_run_usec > 0)?now - pm->last_run_usec:0ULL); - pm->last_run_usec = now; - - now = now_monotonic_usec(); - pm->duration = now - pm->last_run_usec; + pm->enabled = !pm->func(localhost->rrd_update_every, hb_dt); + pm->duration = heartbeat_dt_usec(&hb) - duration; + duration += pm->duration; if(unlikely(netdata_exit)) break; } @@ -118,16 +112,18 @@ void *proc_main(void *ptr) { static RRDSET *st = NULL; if(unlikely(!st)) { - st = rrdset_find_bytype("netdata", "plugin_proc_modules"); + st = rrdset_find_bytype_localhost("netdata", "plugin_proc_modules"); if(!st) { - st = rrdset_create("netdata", "plugin_proc_modules", NULL, "proc", NULL, "NetData Proc Plugin Modules Durations", "milliseconds/run", 132001, rrd_update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("netdata", "plugin_proc_modules", NULL, "proc", NULL + , "NetData Proc Plugin Modules Durations", "milliseconds/run", 132001 + , localhost->rrd_update_every, RRDSET_TYPE_STACKED); for(i = 0 ; proc_modules[i].name ;i++) { struct proc_module *pm = &proc_modules[i]; if(unlikely(!pm->enabled)) continue; - pm->rd = rrddim_add(st, pm->dim, NULL, 1, 1000, RRDDIM_ABSOLUTE); + pm->rd = rrddim_add(st, pm->dim, NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } } } @@ -163,7 +159,7 @@ int get_numa_node_count(void) numa_node_count = 0; char name[FILENAME_MAX + 1]; - snprintfz(name, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/devices/system/node"); + snprintfz(name, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/node"); char *dirname = config_get("plugin:proc:/sys/devices/system/node", "directory to monitor", name); DIR *dir = opendir(dirname); diff --git a/src/plugin_proc_diskspace.c b/src/plugin_proc_diskspace.c index 43e6dd7c5..37133e044 100644 --- a/src/plugin_proc_diskspace.c +++ b/src/plugin_proc_diskspace.c @@ -1,6 +1,8 @@ #include "common.h" -#define DELAULT_EXLUDED_PATHS "/proc/* /sys/* /var/run/user/* /run/user/*" +#define DELAULT_EXLUDED_PATHS "/proc/* /sys/* /var/run/user/* /run/user/* /snap/* /var/lib/docker/*" +#define DEFAULT_EXCLUDED_FILESYSTEMS "" +#define CONFIG_SECTION_DISKSPACE "plugin:proc:diskspace" static struct mountinfo *disk_mountinfo_root = NULL; static int check_for_new_mountpoints_every = 15; @@ -14,17 +16,19 @@ static inline void mountinfo_reload(int force) { mountinfo_free(disk_mountinfo_root); // re-read mountinfo in case something changed - disk_mountinfo_root = mountinfo_read(1); + disk_mountinfo_root = mountinfo_read(0); last_loaded = now; } } -// Data to be stored in DICTIONARY mount_points used by do_disk_space_stats(). +// Data to be stored in DICTIONARY dict_mountpoints used by do_disk_space_stats(). // This DICTIONARY is used to lookup the settings of the mount point on each iteration. struct mount_point_metadata { int do_space; int do_inodes; + int shown_error; + int updated; size_t collected; // the number of times this has been collected @@ -39,41 +43,86 @@ struct mount_point_metadata { RRDDIM *rd_inodes_reserved; }; +static DICTIONARY *dict_mountpoints = NULL; + +#define rrdset_obsolete_and_pointer_null(st) do { if(st) { rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE); st = NULL; } } while(st) + +int mount_point_cleanup(void *entry, void *data) { + (void)data; + + struct mount_point_metadata *mp = (struct mount_point_metadata *)entry; + if(!mp) return 0; + + if(likely(mp->updated)) { + mp->updated = 0; + return 0; + } + + if(likely(mp->collected)) { + mp->collected = 0; + mp->updated = 0; + mp->shown_error = 0; + + mp->rd_space_avail = NULL; + mp->rd_space_used = NULL; + mp->rd_space_reserved = NULL; + + mp->rd_inodes_avail = NULL; + mp->rd_inodes_used = NULL; + mp->rd_inodes_reserved = NULL; + + rrdset_obsolete_and_pointer_null(mp->st_space); + rrdset_obsolete_and_pointer_null(mp->st_inodes); + } + + return 0; +} + static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { const char *family = mi->mount_point; const char *disk = mi->persistent_id; - static DICTIONARY *mount_points = NULL; static SIMPLE_PATTERN *excluded_mountpoints = NULL; + static SIMPLE_PATTERN *excluded_filesystems = NULL; int do_space, do_inodes; - if(unlikely(!mount_points)) { - const char *s; + if(unlikely(!dict_mountpoints)) { SIMPLE_PREFIX_MODE mode = SIMPLE_PATTERN_EXACT; - if(config_exists("plugin:proc:/proc/diskstats", "exclude space metrics on paths") && !config_exists("plugin:proc:diskspace", "exclude space metrics on paths")) { - // the config exists in the old section - s = config_get("plugin:proc:/proc/diskstats", "exclude space metrics on paths", DELAULT_EXLUDED_PATHS); + if(config_move("plugin:proc:/proc/diskstats", "exclude space metrics on paths", CONFIG_SECTION_DISKSPACE, "exclude space metrics on paths") != -1) { + // old configuration, enable backwards compatibility mode = SIMPLE_PATTERN_PREFIX; } - else - s = config_get("plugin:proc:diskspace", "exclude space metrics on paths", DELAULT_EXLUDED_PATHS); - mount_points = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); - excluded_mountpoints = simple_pattern_create(s, mode); + excluded_mountpoints = simple_pattern_create( + config_get(CONFIG_SECTION_DISKSPACE, "exclude space metrics on paths", DELAULT_EXLUDED_PATHS), + mode + ); + + excluded_filesystems = simple_pattern_create( + config_get(CONFIG_SECTION_DISKSPACE, "exclude space metrics on filesystems", DEFAULT_EXCLUDED_FILESYSTEMS), + SIMPLE_PATTERN_EXACT + ); + + dict_mountpoints = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED); } - struct mount_point_metadata *m = dictionary_get(mount_points, mi->mount_point); + struct mount_point_metadata *m = dictionary_get(dict_mountpoints, mi->mount_point); if(unlikely(!m)) { char var_name[4096 + 1]; snprintfz(var_name, 4096, "plugin:proc:diskspace:%s", mi->mount_point); - int def_space = config_get_boolean_ondemand("plugin:proc:diskspace", "space usage for all disks", CONFIG_ONDEMAND_ONDEMAND); - int def_inodes = config_get_boolean_ondemand("plugin:proc:diskspace", "inodes usage for all disks", CONFIG_ONDEMAND_ONDEMAND); + int def_space = config_get_boolean_ondemand(CONFIG_SECTION_DISKSPACE, "space usage for all disks", CONFIG_BOOLEAN_AUTO); + int def_inodes = config_get_boolean_ondemand(CONFIG_SECTION_DISKSPACE, "inodes usage for all disks", CONFIG_BOOLEAN_AUTO); if(unlikely(simple_pattern_matches(excluded_mountpoints, mi->mount_point))) { - def_space = CONFIG_ONDEMAND_NO; - def_inodes = CONFIG_ONDEMAND_NO; + def_space = CONFIG_BOOLEAN_NO; + def_inodes = CONFIG_BOOLEAN_NO; + } + + if(unlikely(simple_pattern_matches(excluded_filesystems, mi->filesystem))) { + def_space = CONFIG_BOOLEAN_NO; + def_inodes = CONFIG_BOOLEAN_NO; } do_space = config_get_boolean_ondemand(var_name, "space usage", def_space); @@ -82,6 +131,8 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { struct mount_point_metadata mp = { .do_space = do_space, .do_inodes = do_inodes, + .shown_error = 0, + .updated = 0, .collected = 0, @@ -96,14 +147,12 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { .rd_inodes_reserved = NULL }; - m = dictionary_set(mount_points, mi->mount_point, &mp, sizeof(struct mount_point_metadata)); - } - else { - do_space = m->do_space; - do_inodes = m->do_inodes; + m = dictionary_set(dict_mountpoints, mi->mount_point, &mp, sizeof(struct mount_point_metadata)); } - if(unlikely(do_space == CONFIG_ONDEMAND_NO && do_inodes == CONFIG_ONDEMAND_NO)) + m->updated = 1; + + if(unlikely(m->do_space == CONFIG_BOOLEAN_NO && m->do_inodes == CONFIG_BOOLEAN_NO)) return; if(unlikely(mi->flags & MOUNTINFO_READONLY && !m->collected)) @@ -111,9 +160,18 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { struct statvfs buff_statvfs; if (statvfs(mi->mount_point, &buff_statvfs) < 0) { - error("Failed statvfs() for '%s' (disk '%s')", mi->mount_point, disk); + if(!m->shown_error) { + error("Failed statvfs() for '%s' (disk '%s', filesystem '%s', root '%s')" + , mi->mount_point + , disk + , mi->filesystem?mi->filesystem:"" + , mi->root?mi->root:"" + ); + m->shown_error = 1; + } return; } + m->shown_error = 0; // logic found at get_fs_usage() in coreutils unsigned long bsize = (buff_statvfs.f_frsize) ? buff_statvfs.f_frsize : buff_statvfs.f_bsize; @@ -150,19 +208,30 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { int rendered = 0; - if(do_space == CONFIG_ONDEMAND_YES || (do_space == CONFIG_ONDEMAND_ONDEMAND && (bavail || breserved_root || bused))) { + if(m->do_space == CONFIG_BOOLEAN_YES || (m->do_space == CONFIG_BOOLEAN_AUTO && (bavail || breserved_root || bused))) { if(unlikely(!m->st_space)) { - m->do_space = CONFIG_ONDEMAND_YES; - m->st_space = rrdset_find_bytype("disk_space", disk); + m->do_space = CONFIG_BOOLEAN_YES; + m->st_space = rrdset_find_bytype_localhost("disk_space", disk); if(unlikely(!m->st_space)) { char title[4096 + 1]; snprintfz(title, 4096, "Disk Space Usage for %s [%s]", family, mi->mount_source); - m->st_space = rrdset_create("disk_space", disk, NULL, family, "disk.space", title, "GB", 2023, update_every, RRDSET_TYPE_STACKED); + m->st_space = rrdset_create_localhost( + "disk_space" + , disk + , NULL + , family + , "disk.space" + , title + , "GB" + , 2023 + , update_every + , RRDSET_TYPE_STACKED + ); } - m->rd_space_avail = rrddim_add(m->st_space, "avail", NULL, bsize, 1024 * 1024 * 1024, RRDDIM_ABSOLUTE); - m->rd_space_used = rrddim_add(m->st_space, "used", NULL, bsize, 1024 * 1024 * 1024, RRDDIM_ABSOLUTE); - m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root", bsize, 1024 * 1024 * 1024, RRDDIM_ABSOLUTE); + m->rd_space_avail = rrddim_add(m->st_space, "avail", NULL, (collected_number)bsize, 1024 * 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + m->rd_space_used = rrddim_add(m->st_space, "used", NULL, (collected_number)bsize, 1024 * 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root", (collected_number)bsize, 1024 * 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(m->st_space); @@ -177,19 +246,30 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { // -------------------------------------------------------------------------- - if(do_inodes == CONFIG_ONDEMAND_YES || (do_inodes == CONFIG_ONDEMAND_ONDEMAND && (favail || freserved_root || fused))) { + if(m->do_inodes == CONFIG_BOOLEAN_YES || (m->do_inodes == CONFIG_BOOLEAN_AUTO && (favail || freserved_root || fused))) { if(unlikely(!m->st_inodes)) { - m->do_inodes = CONFIG_ONDEMAND_YES; - m->st_inodes = rrdset_find_bytype("disk_inodes", disk); + m->do_inodes = CONFIG_BOOLEAN_YES; + m->st_inodes = rrdset_find_bytype_localhost("disk_inodes", disk); if(unlikely(!m->st_inodes)) { char title[4096 + 1]; snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", family, mi->mount_source); - m->st_inodes = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", title, "Inodes", 2024, update_every, RRDSET_TYPE_STACKED); + m->st_inodes = rrdset_create_localhost( + "disk_inodes" + , disk + , NULL + , family + , "disk.inodes" + , title + , "Inodes" + , 2024 + , update_every + , RRDSET_TYPE_STACKED + ); } - m->rd_inodes_avail = rrddim_add(m->st_inodes, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE); - m->rd_inodes_used = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRDDIM_ABSOLUTE); - m->rd_inodes_reserved = rrddim_add(m->st_inodes, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE); + m->rd_inodes_avail = rrddim_add(m->st_inodes, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + m->rd_inodes_used = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + m->rd_inodes_reserved = rrddim_add(m->st_inodes, "reserved_for_root", "reserved for root", 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(m->st_inodes); @@ -221,30 +301,23 @@ void *proc_diskspace_main(void *ptr) { int vdo_cpu_netdata = config_get_boolean("plugin:proc", "netdata server resources", 1); - int update_every = (int)config_get_number("plugin:proc:diskspace", "update every", rrd_update_every); - if(update_every < rrd_update_every) - update_every = rrd_update_every; + int update_every = (int)config_get_number(CONFIG_SECTION_DISKSPACE, "update every", localhost->rrd_update_every); + if(update_every < localhost->rrd_update_every) + update_every = localhost->rrd_update_every; - check_for_new_mountpoints_every = (int)config_get_number("plugin:proc:diskspace", "check for new mount points every", check_for_new_mountpoints_every); + check_for_new_mountpoints_every = (int)config_get_number(CONFIG_SECTION_DISKSPACE, "check for new mount points every", check_for_new_mountpoints_every); if(check_for_new_mountpoints_every < update_every) check_for_new_mountpoints_every = update_every; struct rusage thread; - usec_t last = 0, dt = 0; + usec_t duration = 0; usec_t step = update_every * USEC_PER_SEC; + heartbeat_t hb; + heartbeat_init(&hb); for(;;) { - usec_t now = now_monotonic_usec(); - usec_t next = now - (now % step) + step; - - dt = (last)?now - last:0; - - while(now < next) { - sleep_usec(next - now); - now = now_monotonic_usec(); - } - - last = now; + duration = heartbeat_dt_usec(&hb); + /* usec_t hb_dt = */ heartbeat_next(&hb, step); if(unlikely(netdata_exit)) break; @@ -270,6 +343,8 @@ void *proc_diskspace_main(void *ptr) { if(unlikely(netdata_exit)) break; + dictionary_get_all(dict_mountpoints, mount_point_cleanup, NULL); + if(vdo_cpu_netdata) { static RRDSET *stcpu_thread = NULL, *st_duration = NULL; static RRDDIM *rd_user = NULL, *rd_system = NULL, *rd_duration = NULL; @@ -279,13 +354,23 @@ void *proc_diskspace_main(void *ptr) { getrusage(RUSAGE_THREAD, &thread); if(!stcpu_thread) { - stcpu_thread = rrdset_find("netdata.plugin_diskspace"); - if(!stcpu_thread) stcpu_thread = rrdset_create("netdata", "plugin_diskspace", NULL, "diskspace", NULL - , "NetData Disk Space Plugin CPU usage", "milliseconds/s", 132020 - , update_every, RRDSET_TYPE_STACKED); - - rd_user = rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); - rd_system = rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); + stcpu_thread = rrdset_find_localhost("netdata.plugin_diskspace"); + if(!stcpu_thread) + stcpu_thread = rrdset_create_localhost( + "netdata" + , "plugin_diskspace" + , NULL + , "diskspace" + , NULL + , "NetData Disk Space Plugin CPU usage" + , "milliseconds/s" + , 132020 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_user = rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + rd_system = rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(stcpu_thread); @@ -297,17 +382,27 @@ void *proc_diskspace_main(void *ptr) { // ---------------------------------------------------------------- if(!st_duration) { - st_duration = rrdset_find("netdata.plugin_diskspace_dt"); - if(!st_duration) st_duration = rrdset_create("netdata", "plugin_diskspace_dt", NULL, "diskspace", NULL - , "NetData Disk Space Plugin Duration", "milliseconds/run", 132021 - , update_every, RRDSET_TYPE_AREA); - - rd_duration = rrddim_add(st_duration, "duration", NULL, 1, 1000, RRDDIM_ABSOLUTE); + st_duration = rrdset_find_localhost("netdata.plugin_diskspace_dt"); + if(!st_duration) + st_duration = rrdset_create_localhost( + "netdata" + , "plugin_diskspace_dt" + , NULL + , "diskspace" + , NULL + , "NetData Disk Space Plugin Duration" + , "milliseconds/run" + , 132021 + , update_every + , RRDSET_TYPE_AREA + ); + + rd_duration = rrddim_add(st_duration, "duration", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st_duration); - rrddim_set_by_pointer(st_duration, rd_duration, dt); + rrddim_set_by_pointer(st_duration, rd_duration, duration); rrdset_done(st_duration); // ---------------------------------------------------------------- diff --git a/src/plugin_tc.c b/src/plugin_tc.c index 0fa595320..7dcfedb33 100644 --- a/src/plugin_tc.c +++ b/src/plugin_tc.c @@ -1,7 +1,6 @@ #include "common.h" -#define RRD_TYPE_TC "tc" -#define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC) +#define RRD_TYPE_TC "tc" // ---------------------------------------------------------------------------- // /sbin/tc processor @@ -25,6 +24,9 @@ struct tc_class { char hasparent; char isleaf; + char isqdisc; + char render; + unsigned long long bytes; unsigned long long packets; unsigned long long dropped; @@ -68,6 +70,7 @@ struct tc_device { char enabled_dropped; char enabled_tokens; char enabled_ctokens; + char enabled_all_classes_qdiscs; RRDSET *st_bytes; RRDSET *st_packets; @@ -184,103 +187,171 @@ static inline void tc_device_classes_cleanup(struct tc_device *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, enable_tokens = -1, enable_ctokens = -1; + static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1, enable_tokens = -1, enable_ctokens = -1, enabled_all_classes_qdiscs = -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); - enable_tokens = config_get_boolean_ondemand("plugin:tc", "enable tokens charts for all interfaces", CONFIG_ONDEMAND_NO); - enable_ctokens = config_get_boolean_ondemand("plugin:tc", "enable ctokens charts for all interfaces", CONFIG_ONDEMAND_NO); + enable_new_interfaces = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_BOOLEAN_YES); + enable_bytes = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_BOOLEAN_AUTO); + enable_packets = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_BOOLEAN_AUTO); + enable_dropped = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_BOOLEAN_AUTO); + enable_tokens = config_get_boolean_ondemand("plugin:tc", "enable tokens charts for all interfaces", CONFIG_BOOLEAN_NO); + enable_ctokens = config_get_boolean_ondemand("plugin:tc", "enable ctokens charts for all interfaces", CONFIG_BOOLEAN_NO); + enabled_all_classes_qdiscs = config_get_boolean_ondemand("plugin:tc", "enable show all classes and qdiscs for all interfaces", CONFIG_BOOLEAN_NO); + } + + if(unlikely(d->enabled == (char)-1)) { + char var_name[CONFIG_MAX_NAME + 1]; + snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id); + + d->enabled = (char)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 = (char)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 = (char)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 = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped); + + snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id); + d->enabled_tokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens); + + snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id); + d->enabled_ctokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); + + snprintfz(var_name, CONFIG_MAX_NAME, "show all classes for %s", d->id); + d->enabled_all_classes_qdiscs = (char)config_get_boolean_ondemand("plugin:tc", var_name, enabled_all_classes_qdiscs); } // we only need to add leaf classes - struct tc_class *c, *x; + struct tc_class *c, *x /*, *root = NULL */; unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0, tokens_sum = 0, ctokens_sum = 0; - int active_classes = 0; + int active_nodes = 0, updated_classes = 0, updated_qdiscs = 0; + + // prepare all classes + // we set reasonable defaults for the rest of the code below - // set all classes for(c = d->classes ; c ; c = c->next) { - c->isleaf = 1; - c->hasparent = 0; + c->render = 0; // do not render this class + + c->isleaf = 1; // this is a leaf class + c->hasparent = 0; // without a parent if(unlikely(!c->updated)) - c->unupdated++; - else - c->unupdated = 0; + c->unupdated++; // increase its unupdated counter + else { + c->unupdated = 0; // reset its unupdated counter + + // count how many of each kind + if(c->isqdisc) + updated_qdiscs++; + else + updated_classes++; + } } - // mark the classes as leafs and parents - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + if(unlikely(!d->enabled || (!updated_classes && !updated_qdiscs))) { + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. It is not enabled/updated.", d->name?d->name:d->id); + tc_device_classes_cleanup(d); + return; + } - for(x = d->classes ; x ; x = x->next) { - if(unlikely(!x->updated)) continue; + if(unlikely(updated_classes && updated_qdiscs)) { + error("TC: device '%s' has active both classes (%d) and qdiscs (%d). Will render only qdiscs.", d->id, updated_classes, updated_qdiscs); - if(unlikely(c == x)) continue; + // set all classes to !updated + for(c = d->classes ; c ; c = c->next) + if(unlikely(!c->isqdisc && c->updated)) + c->updated = 0; - 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; - } - } + updated_classes = 0; } - // debugging only - /* - if(unlikely(debug_flags & D_TC_LOOP)) { - 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?d->name:d->id, c->id, c->isleaf, c->hasparent, c->parentid?c->parentid:"(unset)"); + // mark the classes as leafs and parents + // + // TC is hierarchical: + // - classes can have other classes in them + // - the same is true for qdiscs (i.e. qdiscs have classes, that have other qdiscs) + // + // we need to present a chart with leaf nodes only, so that the sum + // of all dimensions of the chart, will be the total utilization + // of the interface. + // + // here we try to find the ones we need to report + // by default all nodes are marked with: isleaf = 1 (see above) + // + // so, here we remove the isleaf flag from nodes in the middle + // and we add the hasparent flag to leaf nodes we found their parent + if(likely(!d->enabled_all_classes_qdiscs)) { + for(c = d->classes; c; c = c->next) { + if(unlikely(!c->updated)) continue; + + //debug(D_TC_LOOP, "TC: In device '%s', %s '%s' has leafid: '%s' and parentid '%s'.", + // d->id, + // c->isqdisc?"qdisc":"class", + // c->id, + // c->leafid?c->leafid:"NULL", + // c->parentid?c->parentid:"NULL"); + + // find if c is leaf or not + for(x = d->classes; x; x = x->next) { + if(unlikely(!x->updated || c == x || !x->parentid)) continue; + + // classes have both parentid and leafid + // qdiscs have only parentid + // the following works for both (it is an OR) + + if((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', %s '%s' (leafid: '%s') has as leaf %s '%s' (parentid: '%s').", d->name?d->name:d->id, c->isqdisc?"qdisc":"class", c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->isqdisc?"qdisc":"class", x->name?x->name:x->id, x->parentid?x->parentid:x->id); + c->isleaf = 0; + x->hasparent = 1; + } + } } } - */ - // 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(unlikely(c->updated && c->isleaf && c->hasparent)) { - active_classes++; + if(unlikely(!c->updated)) continue; + + // debug(D_TC_LOOP, "TC: device '%s', %s '%s' isleaf=%d, hasparent=%d", d->id, (c->isqdisc)?"qdisc":"class", c->id, c->isleaf, c->hasparent); + + if(unlikely((c->isleaf && c->hasparent) || d->enabled_all_classes_qdiscs)) { + c->render = 1; + active_nodes++; bytes_sum += c->bytes; packets_sum += c->packets; dropped_sum += c->dropped; tokens_sum += c->tokens; ctokens_sum += c->ctokens; } - } - 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(!c->hasparent)) { + // if(root) error("TC: multiple root class/qdisc for device '%s' (old: '%s', new: '%s')", d->id, root->id, c->id); + // root = c; + // debug(D_TC_LOOP, "TC: found root class/qdisc '%s'", root->id); + //} } - if(unlikely(d->enabled == (char)-1)) { - char var_name[CONFIG_MAX_NAME + 1]; - snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id); - d->enabled = (char)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 = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes); +#ifdef NETDATA_INTERNAL_CHECKS + // dump all the list to see what we know - snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id); - d->enabled_packets = (char)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 = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped); - - snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id); - d->enabled_tokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens); + if(unlikely(debug_flags & D_TC_LOOP)) { + for(c = d->classes ; c ; c = c->next) { + if(c->render) debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, OK", d->name, c->id); + else debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, IGNORE (updated: %d, isleaf: %d, hasparent: %d, parent: %s)", d->name?d->name:d->id, c->id, c->updated, c->isleaf, c->hasparent, c->parentid?c->parentid:"(unset)"); + } + } +#endif - snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id); - d->enabled_ctokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); + if(unlikely(!active_nodes)) { + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No useful classes/qdiscs.", d->name?d->name:d->id); + tc_device_classes_cleanup(d); + return; } - debug(D_TC_LOOP, "TC: evaluating TC device '%s'. enabled = %d/%d (bytes: %d/%d, packets: %d/%d, dropped: %d/%d, tokens: %d/%d, ctokens: %d/%d), classes = %d (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).", + debug(D_TC_LOOP, "TC: evaluating TC device '%s'. enabled = %d/%d (bytes: %d/%d, packets: %d/%d, dropped: %d/%d, tokens: %d/%d, ctokens: %d/%d, all_classes_qdiscs: %d/%d), classes: (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).", d->name?d->name:d->id, d->enabled, enable_new_interfaces, d->enabled_bytes, enable_bytes, @@ -288,7 +359,7 @@ static inline void tc_device_commit(struct tc_device *d) { d->enabled_dropped, enable_dropped, d->enabled_tokens, enable_tokens, d->enabled_ctokens, enable_ctokens, - active_classes, + d->enabled_all_classes_qdiscs, enabled_all_classes_qdiscs, bytes_sum, packets_sum, dropped_sum, @@ -296,287 +367,226 @@ static inline void tc_device_commit(struct tc_device *d) { ctokens_sum ); - if(likely(d->enabled)) { - // -------------------------------------------------------------------- - // bytes + // -------------------------------------------------------------------- + // bytes - if(d->enabled_bytes == CONFIG_ONDEMAND_YES || (d->enabled_bytes == CONFIG_ONDEMAND_ONDEMAND && bytes_sum)) { - d->enabled_bytes = CONFIG_ONDEMAND_YES; + if(d->enabled_bytes == CONFIG_BOOLEAN_YES || (d->enabled_bytes == CONFIG_BOOLEAN_AUTO && bytes_sum)) { + d->enabled_bytes = CONFIG_BOOLEAN_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(d->st_bytes); + if(unlikely(!d->st_bytes)) + d->st_bytes = rrdset_create_localhost(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, localhost->rrd_update_every + , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE + : RRDSET_TYPE_STACKED); - 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; - } + else { + rrdset_next(d->st_bytes); + if(unlikely(d->name_updated)) rrdset_set_name(d->st_bytes, d->name); - // FIXME - // update the family - } + // FIXME + // update the family + } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - if(c->isleaf && c->hasparent) { - 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); + if(unlikely(!c->rd_bytes)) + c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_bytes, c->rd_bytes, 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); + } + rrdset_done(d->st_bytes); + } - rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes); + // -------------------------------------------------------------------- + // 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_bytes->id, c->rd_bytes->id, c->name); - rrddim_set_name(d->st_bytes, c->rd_bytes, c->name); - } - } - } - rrdset_done(d->st_bytes); - } + if(d->enabled_packets == CONFIG_BOOLEAN_YES || (d->enabled_packets == CONFIG_BOOLEAN_AUTO && packets_sum)) { + d->enabled_packets = CONFIG_BOOLEAN_YES; - // -------------------------------------------------------------------- - // 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); - if(unlikely(!d->st_packets)) { - char id[RRD_ID_LENGTH_MAX + 1]; + d->st_packets = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id + , RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010 + , localhost->rrd_update_every, d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE + : RRDSET_TYPE_STACKED); + } + else { + rrdset_next(d->st_packets); + + if(unlikely(d->name_updated)) { 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); - } + rrdset_set_name(d->st_packets, name); } - else { - debug(D_TC_LOOP, "TC: Updating _packets chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_packets); - // FIXME - // update the family - } + // FIXME + // update the family + } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) 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); + if(unlikely(!c->rd_packets)) + c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_packets, c->rd_packets, 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); + } + rrdset_done(d->st_packets); + } - rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets); + // -------------------------------------------------------------------- + // 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_packets->id, c->rd_packets->id, c->name); - rrddim_set_name(d->st_packets, c->rd_packets, c->name); - } - } - } - rrdset_done(d->st_packets); + if(d->enabled_dropped == CONFIG_BOOLEAN_YES || (d->enabled_dropped == CONFIG_BOOLEAN_AUTO && dropped_sum)) { + d->enabled_dropped = CONFIG_BOOLEAN_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_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id + , RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s" + , 7020, localhost->rrd_update_every + , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE + : RRDSET_TYPE_STACKED); } + else { + rrdset_next(d->st_dropped); - // -------------------------------------------------------------------- - // 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]; + if(unlikely(d->name_updated)) { 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); - } + rrdset_set_name(d->st_dropped, name); } - else { - debug(D_TC_LOOP, "TC: Updating _dropped chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_dropped); - // FIXME - // update the family - } + // FIXME + // update the family + } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) 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); + if(unlikely(!c->rd_dropped)) + c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_dropped, c->rd_dropped, 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); + } + rrdset_done(d->st_dropped); + } - rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped); + // -------------------------------------------------------------------- + // tokens - // 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); + if(d->enabled_tokens == CONFIG_BOOLEAN_YES || (d->enabled_tokens == CONFIG_BOOLEAN_AUTO && tokens_sum)) { + d->enabled_tokens = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_tokens)) { + char id[RRD_ID_LENGTH_MAX + 1]; + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id); + + d->st_tokens = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id + , RRD_TYPE_TC ".qos_tokens", "Class Tokens", "tokens", 7030 + , localhost->rrd_update_every, RRDSET_TYPE_LINE); } + else { + rrdset_next(d->st_tokens); - // -------------------------------------------------------------------- - // tokens - - if(d->enabled_tokens == CONFIG_ONDEMAND_YES || (d->enabled_tokens == CONFIG_ONDEMAND_ONDEMAND && tokens_sum)) { - d->enabled_tokens = CONFIG_ONDEMAND_YES; - - if(unlikely(!d->st_tokens)) { - char id[RRD_ID_LENGTH_MAX + 1]; + if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id); - - d->st_tokens = rrdset_find_bytype(RRD_TYPE_TC, id); - if(unlikely(!d->st_tokens)) { - debug(D_TC_LOOP, "TC: Creating new _tokens chart for device '%s'", d->name?d->name:d->id); - d->st_tokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_tokens", "Class Tokens", "tokens", 7030, rrd_update_every, RRDSET_TYPE_LINE); - } + rrdset_set_name(d->st_tokens, name); } - else { - debug(D_TC_LOOP, "TC: Updating _tokens chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_tokens); - // FIXME - // update the family + // FIXME + // update the family + } + + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; + + if(unlikely(!c->rd_tokens)) { + c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE); } + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_tokens, c->rd_tokens, c->name); - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens); + } + rrdset_done(d->st_tokens); + } - if(c->isleaf && c->hasparent) { - if(unlikely(!c->rd_tokens)) { - c->rd_tokens = rrddim_find(d->st_tokens, c->id); - if(unlikely(!c->rd_tokens)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_tokens->id, c->id, c->name); + // -------------------------------------------------------------------- + // ctokens - // new class, we have to add it - c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_tokens->id, c->id); - } + if(d->enabled_ctokens == CONFIG_BOOLEAN_YES || (d->enabled_ctokens == CONFIG_BOOLEAN_AUTO && ctokens_sum)) { + d->enabled_ctokens = CONFIG_BOOLEAN_YES; - rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens); + if(unlikely(!d->st_ctokens)) { + char id[RRD_ID_LENGTH_MAX + 1]; + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id); - // 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_tokens->id, c->rd_tokens->id, c->name); - rrddim_set_name(d->st_tokens, c->rd_tokens, c->name); - } - } - } - rrdset_done(d->st_tokens); + d->st_ctokens = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id + , RRD_TYPE_TC ".qos_ctokens", "Class cTokens", "ctokens", 7040 + , localhost->rrd_update_every, RRDSET_TYPE_LINE); } + else { + debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id); + rrdset_next(d->st_ctokens); - // -------------------------------------------------------------------- - // ctokens - - if(d->enabled_ctokens == CONFIG_ONDEMAND_YES || (d->enabled_ctokens == CONFIG_ONDEMAND_ONDEMAND && ctokens_sum)) { - d->enabled_ctokens = CONFIG_ONDEMAND_YES; - - if(unlikely(!d->st_ctokens)) { - char id[RRD_ID_LENGTH_MAX + 1]; + if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id); - - d->st_ctokens = rrdset_find_bytype(RRD_TYPE_TC, id); - if(unlikely(!d->st_ctokens)) { - debug(D_TC_LOOP, "TC: Creating new _ctokens chart for device '%s'", d->name?d->name:d->id); - d->st_ctokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_ctokens", "Class cTokens", "ctokens", 7040, rrd_update_every, RRDSET_TYPE_LINE); - } + rrdset_set_name(d->st_ctokens, name); } - else { - debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_ctokens); - - // 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_ctokens)) { - c->rd_ctokens = rrddim_find(d->st_ctokens, c->id); - if(unlikely(!c->rd_ctokens)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_ctokens->id, c->id, c->name); + // FIXME + // update the family + } - // new class, we have to add it - c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_ctokens->id, c->id); - } + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens); + if(unlikely(!c->rd_ctokens)) + c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name); - // 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_ctokens->id, c->rd_ctokens->id, c->name); - rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name); - } - } - } - rrdset_done(d->st_ctokens); + rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens); } + rrdset_done(d->st_ctokens); } tc_device_classes_cleanup(d); } -static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name) -{ +static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name) { + if(unlikely(!name || !*name)) return; + struct tc_class *c = tc_class_index_find(d, id, 0); if(likely(c)) { - freez(c->name); - c->name = NULL; + if(likely(c->name)) { + if(!strcmp(c->name, name)) return; + 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); @@ -587,10 +597,15 @@ static inline void tc_device_set_class_name(struct tc_device *d, char *id, char } static inline void tc_device_set_device_name(struct tc_device *d, char *name) { - freez(d->name); - d->name = NULL; + if(unlikely(!name || !*name)) return; + + if(d->name) { + if(!strcmp(d->name, name)) return; + freez(d->name); + d->name = NULL; + } - if(likely(name && *name && strcmp(d->id, name) != 0)) { + if(likely(name && *name && strcmp(d->id, name))) { debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name); d->name = strdupz(name); d->name_updated = 1; @@ -639,7 +654,7 @@ static inline struct tc_device *tc_device_create(char *id) return(d); } -static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char *parentid, char *leafid) +static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char qdisc, char *parentid, char *leafid) { struct tc_class *c = tc_class_index_find(n, id, 0); @@ -655,6 +670,7 @@ static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char c->id = strdupz(id); c->hash = simple_hash(c->id); + c->isqdisc = qdisc; if(parentid && *parentid) { c->parentid = strdupz(parentid); c->parent_hash = simple_hash(c->parentid); @@ -767,6 +783,7 @@ void *tc_main(void *ptr) { uint32_t BEGIN_HASH = simple_hash("BEGIN"); uint32_t END_HASH = simple_hash("END"); + uint32_t QDISC_HASH = simple_hash("qdisc"); uint32_t CLASS_HASH = simple_hash("class"); uint32_t SENT_HASH = simple_hash("Sent"); uint32_t LENDED_HASH = simple_hash("lended:"); @@ -780,7 +797,7 @@ void *tc_main(void *ptr) { #endif uint32_t first_hash; - snprintfz(buffer, TC_LINE_MAX, "%s/tc-qos-helper.sh", config_get("plugins", "plugins directory", PLUGINS_DIR)); + snprintfz(buffer, TC_LINE_MAX, "%s/tc-qos-helper.sh", netdata_configured_plugins_dir); char *tc_script = config_get("plugin:tc", "script to run to get tc values", buffer); for(;1;) { @@ -790,7 +807,7 @@ void *tc_main(void *ptr) { struct tc_device *device = NULL; struct tc_class *class = NULL; - snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, rrd_update_every); + snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, localhost->rrd_update_every); debug(D_TC_LOOP, "executing '%s'", buffer); fp = mypopen(buffer, (pid_t *)&tc_child_pid); @@ -815,25 +832,51 @@ void *tc_main(void *ptr) { first_hash = simple_hash(words[0]); - if(unlikely(device && first_hash == CLASS_HASH && strcmp(words[0], "class") == 0)) { + if(unlikely(device && ((first_hash == CLASS_HASH && strcmp(words[0], "class") == 0) || (first_hash == QDISC_HASH && strcmp(words[0], "qdisc") == 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 + char *type = words[1]; // the class/qdisc type: htb, fq_codel, etc + char *id = words[2]; // the class/qdisc major:minor + char *parent = words[3]; // the word 'parent' or 'root' + char *parentid = words[4]; // parentid + char *leaf = words[5]; // the word 'leaf' + char *leafid = words[6]; // leafid + + int parent_is_root = 0; + int parent_is_parent = 0; + if(likely(parent)) { + parent_is_parent = !strcmp(parent, "parent"); - // we are only interested for HTB classes - //if(strcmp(type, "htb") != 0) continue; + if(!parent_is_parent) + parent_is_root = !strcmp(parent, "root"); + } + + if(likely(type && id && (parent_is_root || parent_is_parent))) { + char qdisc = 0; + + if(first_hash == QDISC_HASH) { + qdisc = 1; - 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(type, "ingress")) { + // we don't want to get the ingress qdisc + // there should be an IFB interface for this + + class = NULL; + continue; + } + + if(parent_is_parent && parentid) { + // eliminate the minor number from parentid + // why: parentid is the id of the parent class + // but major: is also the id of the parent qdisc + + char *s = parentid; + while(*s && *s != ':') s++; + if(*s == ':') s[1] = '\0'; + } + } - if(strcmp(parent, "root") == 0) { + if(parent_is_root) { parentid = NULL; leafid = NULL; } @@ -847,7 +890,7 @@ void *tc_main(void *ptr) { leafid = leafbuf; } - class = tc_class_add(device, id, parentid, leafid); + class = tc_class_add(device, id, qdisc, parentid, leafid); } else { // clear the last class @@ -946,11 +989,13 @@ void *tc_main(void *ptr) { // 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_find_localhost("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); + stcpu = rrdset_create_localhost("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL + , "NetData TC CPU usage", "milliseconds/s", 135000, localhost->rrd_update_every + , RRDSET_TYPE_STACKED); + rrddim_add(stcpu, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(stcpu, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(stcpu); @@ -958,10 +1003,12 @@ void *tc_main(void *ptr) { 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_find_localhost("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); + sttime = rrdset_create_localhost("netdata", "plugin_tc_time", NULL, "tc.helper", NULL + , "NetData TC script execution", "milliseconds/run", 135001 + , localhost->rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(sttime, "run_time", "run time", 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(sttime); @@ -1009,7 +1056,7 @@ void *tc_main(void *ptr) { goto cleanup; } - sleep((unsigned int) rrd_update_every); + sleep((unsigned int) localhost->rrd_update_every); } cleanup: diff --git a/src/plugins_d.c b/src/plugins_d.c index 4b83b5281..7fa19eaf0 100644 --- a/src/plugins_d.c +++ b/src/plugins_d.c @@ -83,302 +83,315 @@ static int pluginsd_split_words(char *str, char **words, int max_words) { return i; } +inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations) { + int enabled = cd->enabled; -void *pluginsd_worker_thread(void *arg) -{ - struct plugind *cd = (struct plugind *)arg; - cd->obsolete = 0; + if(!fp || !enabled) { + cd->enabled = 0; + return 0; + } - char line[PLUGINSD_LINE_MAX + 1]; + size_t count = 0; -#ifdef DETACH_PLUGINS_FROM_NETDATA - usec_t usec = 0, susec = 0; - struct timeval last = {0, 0} , now = {0, 0}; -#endif + char line[PLUGINSD_LINE_MAX + 1]; char *words[MAX_WORDS] = { NULL }; + /* uint32_t HOST_HASH = simple_hash("HOST"); */ 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"); -#endif - size_t count = 0; + RRDSET *st = NULL; + uint32_t hash; - for(;;) { + errno = 0; + clearerr(fp); + + if(unlikely(fileno(fp) == -1)) { + error("PLUGINSD: %s: file is not a valid stream.", cd->fullfilename); + goto cleanup; + } + + while(!ferror(fp)) { if(unlikely(netdata_exit)) break; - FILE *fp = mypopen(cd->cmd, &cd->pid); - if(unlikely(!fp)) { - error("Cannot popen(\"%s\", \"r\").", cd->cmd); + char *r = fgets(line, PLUGINSD_LINE_MAX, fp); + if(unlikely(!r)) { + error("PLUGINSD: %s : read failed.", cd->fullfilename); break; } - info("PLUGINSD: '%s' running on pid %d", cd->fullfilename, cd->pid); + if(unlikely(netdata_exit)) break; - RRDSET *st = NULL; - uint32_t hash; + line[PLUGINSD_LINE_MAX] = '\0'; - while(likely(fgets(line, PLUGINSD_LINE_MAX, fp) != NULL)) { - if(unlikely(netdata_exit)) break; + // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line); - line[PLUGINSD_LINE_MAX] = '\0'; + int w = pluginsd_split_words(line, words, MAX_WORDS); + char *s = words[0]; + if(unlikely(!s || !*s || !w)) { + // debug(D_PLUGINSD, "PLUGINSD: empty line"); + continue; + } - // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line); + // 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]); - int w = pluginsd_split_words(line, words, MAX_WORDS); - char *s = words[0]; - if(unlikely(!s || !*s || !w)) { - // debug(D_PLUGINSD, "PLUGINSD: empty line"); - continue; - } + if(likely(!simple_hash_strcmp(s, "SET", &hash))) { + char *dimension = words[1]; + char *value = words[2]; - // 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]); + if(unlikely(!dimension || !*dimension)) { + error("PLUGINSD: '%s' is requesting a SET on chart '%s' of host '%s', without a dimension. Disabling it.", cd->fullfilename, st->id, host->hostname); + enabled = 0; + break; + } - if(likely(!simple_hash_strcmp(s, "SET", &hash))) { - char *dimension = words[1]; - char *value = words[2]; + if(unlikely(!value || !*value)) value = NULL; - 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(!st)) { + error("PLUGINSD: '%s' is requesting a SET on dimension %s with value %s on host '%s', without a BEGIN. Disabling it.", cd->fullfilename, dimension, value?value:"<nothing>", host->hostname); + enabled = 0; + break; + } - if(unlikely(!value || !*value)) value = NULL; + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) debug(D_PLUGINSD, "PLUGINSD: '%s' is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value?value:"<nothing>"); - 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(value) rrddim_set(st, dimension, strtoll(value, NULL, 0)); + if(unlikely(!id)) { + error("PLUGINSD: '%s' is requesting a BEGIN without a chart id for host '%s'. Disabling it.", cd->fullfilename, host->hostname); + enabled = 0; + break; } - 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(host, id); + if(unlikely(!st)) { + error("PLUGINSD: '%s' is requesting a BEGIN on chart '%s', which does not exist on host '%s'. Disabling it.", cd->fullfilename, id, host->hostname); + enabled = 0; + 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)) { + usec_t microseconds = 0; + if(microseconds_txt && *microseconds_txt) microseconds = str2ull(microseconds_txt); - if(likely(st->counter_done)) { - usec_t microseconds = 0; - if(microseconds_txt && *microseconds_txt) microseconds = str2ull(microseconds_txt); - if(microseconds) rrdset_next_usec(st, microseconds); - else rrdset_next(st); + if(likely(microseconds)) { + if(trust_durations) + rrdset_next_usec_unfiltered(st, microseconds); + else + rrdset_next_usec(st, microseconds); } + else rrdset_next(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; - } + } + else if(likely(hash == END_HASH && !strcmp(s, "END"))) { + if(unlikely(!st)) { + error("PLUGINSD: '%s' is requesting an END, without a BEGIN on host '%s'. Disabling it.", cd->fullfilename, host->hostname); + enabled = 0; + break; + } + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting an END on chart %s", cd->fullfilename, st->id); - 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; - rrdset_done(st); - st = NULL; + count++; + } +/* else if(likely(hash == HOST_HASH && !strcmp(s, "HOST"))) { + char *guid = words[1]; + char *hostname = words[2]; - count++; + if(unlikely(!guid || !*guid)) { + error("PLUGINSD: '%s' is requesting HOST with guid '%s' and hostname '%s', without a guid. Disabling it.", cd->fullfilename, guid?guid:"", hostname?hostname:""); + enabled = 0; + break; } - else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) { - debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename); - st = NULL; + if(unlikely(!hostname || !*hostname)) { + error("PLUGINSD: '%s' is requesting HOST with guid '%s' and hostname '%s', without a hostname. Disabling it.", cd->fullfilename, guid?guid:"", hostname?hostname:""); + enabled = 0; + break; } - 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 = str2i(priority_s); - - int update_every = cd->update_every; - if(likely(update_every_s)) update_every = str2i(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); + host = rrdhost_find_or_create(hostname, guid); + } */ + 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, on host '%s'. Disabling it.", cd->fullfilename, host->hostname); + enabled = 0; + break; } - 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); + int priority = 1000; + if(likely(priority_s)) priority = str2i(priority_s); + + int update_every = cd->update_every; + if(likely(update_every_s)) update_every = str2i(update_every_s); + if(unlikely(!update_every)) update_every = cd->update_every; + + RRDSET_TYPE 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(host, 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(host, type, id, name, family, context, title, units, priority, update_every, chart_type); + cd->update_every = update_every; } - else if(unlikely(hash == DISABLE_HASH && !strcmp(s, "DISABLE"))) { - info("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename); - cd->enabled = 0; - killpid(cd->pid, SIGTERM); + 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, host '%s' and chart '%s'. Disabling it.", cd->fullfilename, host->hostname, st?st->id:"UNSET"); + enabled = 0; 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); + if(unlikely(!st)) { + error("PLUGINSD: '%s' is requesting a DIMENSION, without a CHART, on host '%s'. Disabling it.", cd->fullfilename, host->hostname); + enabled = 0; + break; } - 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); - now_realtime_timeval(&now); - if(unlikely(!usec && !susec)) { - // our first run - susec = cd->rrd_update_every * USEC_PER_SEC; + 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(rrdset_flag_check(st, RRDSET_FLAG_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:"" + , rrd_algorithm_name(rrd_algorithm_id(algorithm)) + , multiplier + , divisor + , options?options:"" + ); + + RRDDIM *rd = rrddim_find(st, id); + if(unlikely(!rd)) { + rd = rrddim_add(st, id, name, multiplier, divisor, rrd_algorithm_id(algorithm)); + rrddim_flag_clear(rd, RRDDIM_FLAG_HIDDEN); + rrddim_flag_clear(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS); + if(options && *options) { + if(strstr(options, "hidden") != NULL) rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN); + if(strstr(options, "noreset") != NULL) rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS); + if(strstr(options, "nooverflow") != NULL) rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS); } - else { - // second+ run - usec = dt_usec(&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 * USEC_PER_SEC / 2ULL))) susec = (rrd_update_every * USEC_PER_SEC) - usec; - else susec = rrd_update_every * USEC_PER_SEC / 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); - memmove(&last, &now, 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; } + else if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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"))) { + info("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename); + enabled = 0; + break; } - if(likely(count)) { - cd->successful_collections += count; - cd->serial_failures = 0; + else { + error("PLUGINSD: '%s' is sending command '%s' which is not known by netdata, for host '%s'. Disabling it.", cd->fullfilename, s, host->hostname); + enabled = 0; + break; } - else - cd->serial_failures++; + } + +cleanup: + cd->enabled = enabled; + + if(likely(count)) { + cd->successful_collections += count; + cd->serial_failures = 0; + } + else + cd->serial_failures++; + + return count; +} + +void *pluginsd_worker_thread(void *arg) { + struct plugind *cd = (struct plugind *)arg; + cd->obsolete = 0; + + size_t count = 0; + + for(;;) { + 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); + + count = pluginsd_process(localhost, cd, fp, 0); + error("PLUGINSD: plugin '%s' disconnected.", cd->fullfilename); + + killpid(cd->pid, SIGTERM); 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(unlikely(netdata_exit)) break; else if(code != 0) { // the plugin reports failure @@ -442,24 +455,23 @@ void *pluginsd_main(void *ptr) { 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); + int automatic_run = config_get_boolean(CONFIG_SECTION_PLUGINS, "enable running new plugins", 1); + int scan_frequency = (int) config_get_number(CONFIG_SECTION_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); + // config_get_boolean(CONFIG_SECTION_PLUGINS, "apps", 1); if(scan_frequency < 1) scan_frequency = 1; for(;;) { if(unlikely(netdata_exit)) break; - dir = opendir(dir_name); + dir = opendir(netdata_configured_plugins_dir); if(unlikely(!dir)) { - error("Cannot open directory '%s'.", dir_name); + error("Cannot open directory '%s'.", netdata_configured_plugins_dir); goto cleanup; } @@ -479,7 +491,7 @@ void *pluginsd_main(void *ptr) { 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); + int enabled = config_get_boolean(CONFIG_SECTION_PLUGINS, pluginname, automatic_run); if(unlikely(!enabled)) { debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name); @@ -503,10 +515,10 @@ void *pluginsd_main(void *ptr) { 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); + snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", netdata_configured_plugins_dir, cd->filename); cd->enabled = enabled; - cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every); + cd->update_every = (int) config_get_number(cd->id, "update every", localhost->rrd_update_every); cd->started_t = now_realtime_sec(); char *def = ""; diff --git a/src/plugins_d.h b/src/plugins_d.h index 3c74355a3..d34c4030c 100644 --- a/src/plugins_d.h +++ b/src/plugins_d.h @@ -11,7 +11,7 @@ struct plugind { 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 + char cmd[PLUGINSD_CMD_MAX+1]; // the command that it executes pid_t pid; pthread_t thread; @@ -34,5 +34,6 @@ struct plugind { extern struct plugind *pluginsd_root; extern void *pluginsd_main(void *ptr); +extern size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations); #endif /* NETDATA_PLUGINS_D_H */ diff --git a/src/proc_diskstats.c b/src/proc_diskstats.c index 9ccac6dc7..a1b4072dd 100644 --- a/src/proc_diskstats.c +++ b/src/proc_diskstats.c @@ -6,6 +6,9 @@ #define DISK_TYPE_PARTITION 2 #define DISK_TYPE_CONTAINER 3 +#define CONFIG_SECTION_DISKSTATS "plugin:proc:/proc/diskstats" +#define DELAULT_EXLUDED_DISKS "loop* ram*" + static struct disk { char *disk; // the name of the disk (sda, sdb, etc) unsigned long major; @@ -25,9 +28,24 @@ static struct disk { int do_util; int do_backlog; + int updated; + + RRDSET *st_avgsz; + RRDSET *st_await; + RRDSET *st_backlog; + RRDSET *st_io; + RRDSET *st_iotime; + RRDSET *st_mops; + RRDSET *st_ops; + RRDSET *st_qops; + RRDSET *st_svctm; + RRDSET *st_util; + struct disk *next; } *disk_root = NULL; +#define rrdset_obsolete_and_pointer_null(st) do { if(st) { rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE); st = NULL; } } while(st) + 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] = ""; @@ -46,7 +64,7 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis // not found // create a new disk structure - d = (struct disk *)mallocz(sizeof(struct disk)); + d = (struct disk *)callocz(1, sizeof(struct disk)); d->disk = strdupz(disk); d->major = major; @@ -72,8 +90,8 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis // 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)); + snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/dev/block/%lu:%lu/%s"); + snprintfz(path_find_block_device, FILENAME_MAX, "%s", config_get(CONFIG_SECTION_DISKSTATS, "path to get block device infos", buffer)); } // find if it is a partition @@ -126,12 +144,12 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis // find the disk sector size if(unlikely(!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)); + snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/block/%s/queue/hw_sector_size"); + snprintfz(path_to_get_hw_sector_size, FILENAME_MAX, "%s", config_get(CONFIG_SECTION_DISKSTATS, "path to get h/w sector size", buffer)); } if(unlikely(!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)); + snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_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(CONFIG_SECTION_DISKSTATS, "path to get h/w sector size for partitions", buffer)); } { @@ -170,7 +188,7 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis } static inline int is_major_enabled(int major) { - static char *major_configs = NULL; + static int8_t *major_configs = NULL; static size_t major_size = 0; if(major < 0) return 1; @@ -178,7 +196,7 @@ static inline int is_major_enabled(int major) { size_t wanted_size = (size_t)major + 1; if(major_size < wanted_size) { - major_configs = reallocz(major_configs, wanted_size); + major_configs = reallocz(major_configs, wanted_size * sizeof(int8_t)); size_t i; for(i = major_size; i < wanted_size ; i++) @@ -190,43 +208,40 @@ static inline int is_major_enabled(int major) { if(major_configs[major] == -1) { char buffer[CONFIG_MAX_NAME + 1]; snprintfz(buffer, CONFIG_MAX_NAME, "performance metrics for disks with major %d", major); - major_configs[major] = (char)config_get_boolean("plugin:proc:/proc/diskstats", buffer, 1); + major_configs[major] = (char)config_get_boolean(CONFIG_SECTION_DISKSTATS, buffer, 1); } - return major_configs[major]; + return (int)major_configs[major]; } int do_proc_diskstats(int update_every, usec_t dt) { - (void)dt; - static procfile *ff = NULL; - 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_ONDEMAND, - global_enable_performance_for_partitions = CONFIG_ONDEMAND_NO, - 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, + static int global_enable_new_disks_detected_at_runtime = CONFIG_BOOLEAN_YES, + global_enable_performance_for_physical_disks = CONFIG_BOOLEAN_AUTO, + global_enable_performance_for_virtual_disks = CONFIG_BOOLEAN_AUTO, + global_enable_performance_for_partitions = CONFIG_BOOLEAN_NO, + global_do_io = CONFIG_BOOLEAN_AUTO, + global_do_ops = CONFIG_BOOLEAN_AUTO, + global_do_mops = CONFIG_BOOLEAN_AUTO, + global_do_iotime = CONFIG_BOOLEAN_AUTO, + global_do_qops = CONFIG_BOOLEAN_AUTO, + global_do_util = CONFIG_BOOLEAN_AUTO, + global_do_backlog = CONFIG_BOOLEAN_AUTO, 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_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_enable_new_disks_detected_at_runtime = config_get_boolean(CONFIG_SECTION_DISKSTATS, "enable new disks detected at runtime", global_enable_new_disks_detected_at_runtime); + global_enable_performance_for_physical_disks = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "performance metrics for physical disks", global_enable_performance_for_physical_disks); + global_enable_performance_for_virtual_disks = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "performance metrics for virtual disks", global_enable_performance_for_virtual_disks); + global_enable_performance_for_partitions = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "performance metrics for partitions", global_enable_performance_for_partitions); + + global_do_io = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "bandwidth for all disks", global_do_io); + global_do_ops = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "operations for all disks", global_do_ops); + global_do_mops = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "merged operations for all disks", global_do_mops); + global_do_iotime = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "i/o time for all disks", global_do_iotime); + global_do_qops = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "queued operations for all disks", global_do_qops); + global_do_util = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "utilization percentage for all disks", global_do_util); + global_do_backlog = config_get_boolean_ondemand(CONFIG_SECTION_DISKSTATS, "backlog for all disks", global_do_backlog); globals_initialized = 1; } @@ -235,8 +250,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!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); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/diskstats"); + ff = procfile_open(config_get(CONFIG_SECTION_DISKSTATS, "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); } if(unlikely(!ff)) return 0; @@ -318,7 +333,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { // get a disk structure for the disk struct disk *d = get_disk(major, minor, disk); - + d->updated = 1; // -------------------------------------------------------------------------- // Set its family based on mount point @@ -331,25 +346,41 @@ int do_proc_diskstats(int update_every, usec_t dt) { // Check the configuration for the device if(unlikely(!d->configured)) { + d->configured = 1; + + static SIMPLE_PATTERN *excluded_disks = NULL; + + if(unlikely(!excluded_disks)) { + excluded_disks = simple_pattern_create( + config_get(CONFIG_SECTION_DISKSTATS, "exclude disks", DELAULT_EXLUDED_DISKS), + SIMPLE_PATTERN_EXACT + ); + } + + int def_enable = global_enable_new_disks_detected_at_runtime; + + if(def_enable != CONFIG_BOOLEAN_NO && simple_pattern_matches(excluded_disks, disk)) + def_enable = CONFIG_BOOLEAN_NO; + 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(unlikely(def_enable == CONFIG_ONDEMAND_NO)) { + def_enable = config_get_boolean_ondemand(var_name, "enable", def_enable); + if(unlikely(def_enable == CONFIG_BOOLEAN_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_io = CONFIG_BOOLEAN_NO; + d->do_ops = CONFIG_BOOLEAN_NO; + d->do_mops = CONFIG_BOOLEAN_NO; + d->do_iotime = CONFIG_BOOLEAN_NO; + d->do_qops = CONFIG_BOOLEAN_NO; + d->do_util = CONFIG_BOOLEAN_NO; + d->do_backlog = CONFIG_BOOLEAN_NO; } else { // this disk is enabled // check its direct settings - int def_performance = CONFIG_ONDEMAND_ONDEMAND; + int def_performance = CONFIG_BOOLEAN_AUTO; // since this is 'on demand' we can figure the performance settings // based on the type of disk @@ -380,16 +411,16 @@ int do_proc_diskstats(int update_every, usec_t dt) { // 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; + int ddo_io = CONFIG_BOOLEAN_NO, + ddo_ops = CONFIG_BOOLEAN_NO, + ddo_mops = CONFIG_BOOLEAN_NO, + ddo_iotime = CONFIG_BOOLEAN_NO, + ddo_qops = CONFIG_BOOLEAN_NO, + ddo_util = CONFIG_BOOLEAN_NO, + ddo_backlog = CONFIG_BOOLEAN_NO; // we enable individual performance charts only when def_performance is not disabled - if(unlikely(def_performance != CONFIG_ONDEMAND_NO)) { + if(unlikely(def_performance != CONFIG_BOOLEAN_NO)) { ddo_io = global_do_io, ddo_ops = global_do_ops, ddo_mops = global_do_mops, @@ -407,144 +438,216 @@ int do_proc_diskstats(int update_every, usec_t dt) { 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); } - - 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(unlikely(!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); + if(d->do_io == CONFIG_BOOLEAN_YES || (d->do_io == CONFIG_BOOLEAN_AUTO && (readsectors || writesectors))) { + d->do_io = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_io)) { + d->st_io = rrdset_create_localhost( + RRD_TYPE_DISK + , disk + , NULL + , family + , "disk.io" + , "Disk I/O Bandwidth" + , "kilobytes/s" + , 2000 + , update_every + , RRDSET_TYPE_AREA + ); + + rrddim_add(d->st_io, "reads", NULL, d->sector_size, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(d->st_io, "writes", NULL, d->sector_size * -1, 1024, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_io); - last_readsectors = rrddim_set(st, "reads", readsectors); - last_writesectors = rrddim_set(st, "writes", writesectors); - rrdset_done(st); + last_readsectors = rrddim_set(d->st_io, "reads", readsectors); + last_writesectors = rrddim_set(d->st_io, "writes", writesectors); + rrdset_done(d->st_io); } // -------------------------------------------------------------------- - 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(unlikely(!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); + if(d->do_ops == CONFIG_BOOLEAN_YES || (d->do_ops == CONFIG_BOOLEAN_AUTO && (reads || writes))) { + d->do_ops = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_ops)) { + d->st_ops = rrdset_create_localhost( + "disk_ops" + , disk + , NULL + , family + , "disk.ops" + , "Disk Completed I/O Operations" + , "operations/s" + , 2001 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_ops, RRDSET_FLAG_DETAIL); + + rrddim_add(d->st_ops, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(d->st_ops, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_ops); - last_reads = rrddim_set(st, "reads", reads); - last_writes = rrddim_set(st, "writes", writes); - rrdset_done(st); + last_reads = rrddim_set(d->st_ops, "reads", reads); + last_writes = rrddim_set(d->st_ops, "writes", writes); + rrdset_done(d->st_ops); } // -------------------------------------------------------------------- - 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(unlikely(!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); + if(d->do_qops == CONFIG_BOOLEAN_YES || (d->do_qops == CONFIG_BOOLEAN_AUTO && queued_ios)) { + d->do_qops = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_qops)) { + d->st_qops = rrdset_create_localhost( + "disk_qops" + , disk + , NULL + , family + , "disk.qops" + , "Disk Current I/O Operations" + , "operations" + , 2002 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_qops, RRDSET_FLAG_DETAIL); + + rrddim_add(d->st_qops, "operations", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); + else rrdset_next(d->st_qops); - rrddim_set(st, "operations", queued_ios); - rrdset_done(st); + rrddim_set(d->st_qops, "operations", queued_ios); + rrdset_done(d->st_qops); } // -------------------------------------------------------------------- - 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(unlikely(!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); + if(d->do_backlog == CONFIG_BOOLEAN_YES || (d->do_backlog == CONFIG_BOOLEAN_AUTO && backlog_ms)) { + d->do_backlog = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_backlog)) { + d->st_backlog = rrdset_create_localhost( + "disk_backlog" + , disk + , NULL + , family + , "disk.backlog" + , "Disk Backlog" + , "backlog (ms)" + , 2003 + , update_every + , RRDSET_TYPE_AREA + ); + + rrdset_flag_set(d->st_backlog, RRDSET_FLAG_DETAIL); + + rrddim_add(d->st_backlog, "backlog", NULL, 1, 10, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_backlog); - rrddim_set(st, "backlog", backlog_ms); - rrdset_done(st); + rrddim_set(d->st_backlog, "backlog", backlog_ms); + rrdset_done(d->st_backlog); } // -------------------------------------------------------------------- - 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(unlikely(!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); + if(d->do_util == CONFIG_BOOLEAN_YES || (d->do_util == CONFIG_BOOLEAN_AUTO && busy_ms)) { + d->do_util = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_util)) { + d->st_util = rrdset_create_localhost( + "disk_util" + , disk + , NULL + , family + , "disk.util" + , "Disk Utilization Time" + , "% of time working" + , 2004 + , update_every + , RRDSET_TYPE_AREA + ); + + rrdset_flag_set(d->st_util, RRDSET_FLAG_DETAIL); + + rrddim_add(d->st_util, "utilization", NULL, 1, 10, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_util); - last_busy_ms = rrddim_set(st, "utilization", busy_ms); - rrdset_done(st); + last_busy_ms = rrddim_set(d->st_util, "utilization", busy_ms); + rrdset_done(d->st_util); } // -------------------------------------------------------------------- - 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(unlikely(!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); + if(d->do_mops == CONFIG_BOOLEAN_YES || (d->do_mops == CONFIG_BOOLEAN_AUTO && (mreads || mwrites))) { + d->do_mops = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_mops)) { + d->st_mops = rrdset_create_localhost( + "disk_mops" + , disk + , NULL + , family + , "disk.mops" + , "Disk Merged Operations" + , "merged operations/s" + , 2021 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_mops, RRDSET_FLAG_DETAIL); + + rrddim_add(d->st_mops, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(d->st_mops, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_mops); - rrddim_set(st, "reads", mreads); - rrddim_set(st, "writes", mwrites); - rrdset_done(st); + rrddim_set(d->st_mops, "reads", mreads); + rrddim_set(d->st_mops, "writes", mwrites); + rrdset_done(d->st_mops); } // -------------------------------------------------------------------- - 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(unlikely(!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); + if(d->do_iotime == CONFIG_BOOLEAN_YES || (d->do_iotime == CONFIG_BOOLEAN_AUTO && (readms || writems))) { + d->do_iotime = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_iotime)) { + d->st_iotime = rrdset_create_localhost( + "disk_iotime" + , disk + , NULL + , family + , "disk.iotime" + , "Disk Total I/O Time" + , "milliseconds/s" + , 2022 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_iotime, RRDSET_FLAG_DETAIL); + + rrddim_add(d->st_iotime, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(d->st_iotime, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } - else rrdset_next(st); + else rrdset_next(d->st_iotime); - last_readms = rrddim_set(st, "reads", readms); - last_writems = rrddim_set(st, "writes", writems); - rrdset_done(st); + last_readms = rrddim_set(d->st_iotime, "reads", readms); + last_writems = rrddim_set(d->st_iotime, "writes", writems); + rrdset_done(d->st_iotime); } // -------------------------------------------------------------------- @@ -552,54 +655,127 @@ int do_proc_diskstats(int update_every, usec_t dt) { // only if this is not the first time we run if(likely(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(unlikely(!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); + if( (d->do_iotime == CONFIG_BOOLEAN_YES || (d->do_iotime == CONFIG_BOOLEAN_AUTO && (readms || writems))) && + (d->do_ops == CONFIG_BOOLEAN_YES || (d->do_ops == CONFIG_BOOLEAN_AUTO && (reads || writes)))) { + + if(unlikely(!d->st_await)) { + d->st_await = rrdset_create_localhost( + "disk_await" + , disk + , NULL + , family + , "disk.await" + , "Average Completed I/O Operation Time" + , "ms per operation" + , 2005 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_await, RRDSET_FLAG_DETAIL); + + rrddim_add(d->st_await, "reads", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(d->st_await, "writes", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); + else rrdset_next(d->st_await); - 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); + rrddim_set(d->st_await, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0); + rrddim_set(d->st_await, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0); + rrdset_done(d->st_await); } - 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(unlikely(!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); + if( (d->do_io == CONFIG_BOOLEAN_YES || (d->do_io == CONFIG_BOOLEAN_AUTO && (readsectors || writesectors))) && + (d->do_ops == CONFIG_BOOLEAN_YES || (d->do_ops == CONFIG_BOOLEAN_AUTO && (reads || writes)))) { + + if(unlikely(!d->st_avgsz)) { + d->st_avgsz = rrdset_create_localhost( + "disk_avgsz" + , disk + , NULL + , family + , "disk.avgsz" + , "Average Completed I/O Operation Bandwidth" + , "kilobytes per operation" + , 2006 + , update_every + , RRDSET_TYPE_AREA + ); + + rrdset_flag_set(d->st_avgsz, RRDSET_FLAG_DETAIL); + + rrddim_add(d->st_avgsz, "reads", NULL, d->sector_size, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(d->st_avgsz, "writes", NULL, d->sector_size * -1, 1024, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); + else rrdset_next(d->st_avgsz); - 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); + rrddim_set(d->st_avgsz, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0); + rrddim_set(d->st_avgsz, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0); + rrdset_done(d->st_avgsz); } - 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(unlikely(!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); + if( (d->do_util == CONFIG_BOOLEAN_YES || (d->do_util == CONFIG_BOOLEAN_AUTO && busy_ms)) && + (d->do_ops == CONFIG_BOOLEAN_YES || (d->do_ops == CONFIG_BOOLEAN_AUTO && (reads || writes)))) { + + if(unlikely(!d->st_svctm)) { + d->st_svctm = rrdset_create_localhost( + "disk_svctm" + , disk + , NULL + , family + , "disk.svctm" + , "Average Service Time" + , "ms per operation" + , 2007 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_svctm, RRDSET_FLAG_DETAIL); + + rrddim_add(d->st_svctm, "svctm", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } - else rrdset_next(st); + else rrdset_next(d->st_svctm); + + rrddim_set(d->st_svctm, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) : 0); + rrdset_done(d->st_svctm); + } + } + } - 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); + // cleanup removed disks + + struct disk *d = disk_root, *last = NULL; + while(d) { + if(unlikely(!d->updated)) { + struct disk *t = d; + + rrdset_obsolete_and_pointer_null(d->st_avgsz); + rrdset_obsolete_and_pointer_null(d->st_await); + rrdset_obsolete_and_pointer_null(d->st_backlog); + rrdset_obsolete_and_pointer_null(d->st_io); + rrdset_obsolete_and_pointer_null(d->st_iotime); + rrdset_obsolete_and_pointer_null(d->st_mops); + rrdset_obsolete_and_pointer_null(d->st_ops); + rrdset_obsolete_and_pointer_null(d->st_qops); + rrdset_obsolete_and_pointer_null(d->st_svctm); + rrdset_obsolete_and_pointer_null(d->st_util); + + if(d == disk_root) { + disk_root = d = d->next; + last = NULL; + } + else if(last) { + last->next = d = d->next; } + + freez(t->disk); + freez(t->mount_point); + freez(t); + } + else { + d->updated = 0; + last = d; + d = d->next; } } diff --git a/src/proc_interrupts.c b/src/proc_interrupts.c index f663c0fdd..082e1f57b 100644 --- a/src/proc_interrupts.c +++ b/src/proc_interrupts.c @@ -59,7 +59,7 @@ int do_proc_interrupts(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/interrupts"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/interrupts"); ff = procfile_open(config_get("plugin:proc:/proc/interrupts", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); } if(unlikely(!ff)) @@ -146,8 +146,9 @@ int do_proc_interrupts(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("system", "interrupts"); - if(unlikely(!st)) st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED); + st = rrdset_find_bytype_localhost("system", "interrupts"); + if(unlikely(!st)) st = rrdset_create_localhost("system", "interrupts", NULL, "interrupts", NULL, "System interrupts" + , "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED); else rrdset_next(st); for(l = 0; l < lines ;l++) { @@ -159,7 +160,7 @@ int do_proc_interrupts(int update_every, usec_t dt) { if(unlikely(!irr->rd || strncmp(irr->rd->name, irr->name, MAX_INTERRUPT_NAME) != 0)) { irr->rd = rrddim_find(st, irr->id); if(unlikely(!irr->rd)) - irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); + irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); else rrddim_set_name(st, irr->rd, irr->name); @@ -181,11 +182,12 @@ int do_proc_interrupts(int update_every, usec_t dt) { char id[50+1]; snprintfz(id, 50, "cpu%d_interrupts", c); - st = rrdset_find_bytype("cpu", id); + st = rrdset_find_bytype_localhost("cpu", id); if(unlikely(!st)) { char title[100+1]; snprintfz(title, 100, "CPU%d Interrupts", c); - st = rrdset_create("cpu", id, NULL, "interrupts", "cpu.interrupts", title, "interrupts/s", 1100 + c, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("cpu", id, NULL, "interrupts", "cpu.interrupts", title, "interrupts/s", + 1100 + c, update_every, RRDSET_TYPE_STACKED); } else rrdset_next(st); @@ -195,7 +197,7 @@ int do_proc_interrupts(int update_every, usec_t dt) { if(unlikely(!irr->cpu[c].rd)) { irr->cpu[c].rd = rrddim_find(st, irr->id); if(unlikely(!irr->cpu[c].rd)) - irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); + irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); else rrddim_set_name(st, irr->cpu[c].rd, irr->name); } diff --git a/src/proc_loadavg.c b/src/proc_loadavg.c index 4326ffb7d..e7863f114 100644 --- a/src/proc_loadavg.c +++ b/src/proc_loadavg.c @@ -6,12 +6,12 @@ int do_proc_loadavg(int update_every, usec_t dt) { static procfile *ff = NULL; static int do_loadavg = -1, do_all_processes = -1; - static usec_t last_loadavg_usec = 0; + static usec_t next_loadavg_dt = 0; static RRDSET *load_chart = NULL, *processes_chart = NULL; if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/loadavg"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/loadavg"); ff = procfile_open(config_get("plugin:proc:/proc/loadavg", "filename to monitor", filename), " \t,:|/", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) @@ -47,15 +47,18 @@ int do_proc_loadavg(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(last_loadavg_usec <= dt) { + if(next_loadavg_dt <= dt) { if(likely(do_loadavg)) { if(unlikely(!load_chart)) { - load_chart = rrdset_find_byname("system.load"); + load_chart = rrdset_find_byname_localhost("system.load"); if(unlikely(!load_chart)) { - load_chart = 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(load_chart, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE); - rrddim_add(load_chart, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE); - rrddim_add(load_chart, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE); + load_chart = rrdset_create_localhost("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(load_chart, "load1", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(load_chart, "load5", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(load_chart, "load15", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } } else @@ -67,18 +70,20 @@ int do_proc_loadavg(int update_every, usec_t dt) { rrdset_done(load_chart); } - last_loadavg_usec = load_chart->update_every * USEC_PER_SEC; + next_loadavg_dt = load_chart->update_every * USEC_PER_SEC; } - else last_loadavg_usec -= dt; + else next_loadavg_dt -= dt; // -------------------------------------------------------------------- if(likely(do_all_processes)) { if(unlikely(!processes_chart)) { - processes_chart = rrdset_find_byname("system.active_processes"); + processes_chart = rrdset_find_byname_localhost("system.active_processes"); if(unlikely(!processes_chart)) { - processes_chart = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE); - rrddim_add(processes_chart, "active", NULL, 1, 1, RRDDIM_ABSOLUTE); + processes_chart = rrdset_create_localhost("system", "active_processes", NULL, "processes", NULL + , "System Active Processes", "processes", 750, update_every + , RRDSET_TYPE_LINE); + rrddim_add(processes_chart, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } } else rrdset_next(processes_chart); diff --git a/src/proc_meminfo.c b/src/proc_meminfo.c index 19ba8da3c..6b0219cc9 100644 --- a/src/proc_meminfo.c +++ b/src/proc_meminfo.c @@ -55,8 +55,8 @@ int do_proc_meminfo(int update_every, usec_t dt) { if(unlikely(!arl_base)) { do_ram = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1); - do_swap = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "system swap", CONFIG_ONDEMAND_ONDEMAND); - do_hwcorrupt = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_ONDEMAND_ONDEMAND); + do_swap = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "system swap", CONFIG_BOOLEAN_AUTO); + do_hwcorrupt = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_BOOLEAN_AUTO); do_committed = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1); do_writeback = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1); do_kernel = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1); @@ -109,7 +109,7 @@ int do_proc_meminfo(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/meminfo"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/meminfo"); ff = procfile_open(config_get("plugin:proc:/proc/meminfo", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; @@ -140,14 +140,15 @@ int do_proc_meminfo(int update_every, usec_t dt) { unsigned long long MemUsed = MemTotal - MemFree - Cached - Buffers; if(do_ram) { - st = rrdset_find("system.ram"); + st = rrdset_find_localhost("system.ram"); if(!st) { - st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every + , RRDSET_TYPE_STACKED); - rrddim_add(st, "free", 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, "buffers", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(st, "free", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "cached", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "buffers", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -162,16 +163,17 @@ int do_proc_meminfo(int update_every, usec_t dt) { unsigned long long SwapUsed = SwapTotal - SwapFree; - if(SwapTotal || SwapUsed || SwapFree || do_swap == CONFIG_ONDEMAND_YES) { - do_swap = CONFIG_ONDEMAND_YES; + if(SwapTotal || SwapUsed || SwapFree || do_swap == CONFIG_BOOLEAN_YES) { + do_swap = CONFIG_BOOLEAN_YES; - st = rrdset_find("system.swap"); + st = rrdset_find_localhost("system.swap"); if(!st) { - st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED); - st->isdetail = 1; + st = rrdset_create_localhost("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every + , RRDSET_TYPE_STACKED); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "free", NULL, 1, 1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "used", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(st, "free", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -182,15 +184,16 @@ int do_proc_meminfo(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(arl_hwcorrupted->flags & ARL_ENTRY_FLAG_FOUND && (do_hwcorrupt == CONFIG_ONDEMAND_YES || (do_hwcorrupt == CONFIG_ONDEMAND_ONDEMAND && HardwareCorrupted > 0))) { - do_hwcorrupt = CONFIG_ONDEMAND_YES; + if(arl_hwcorrupted->flags & ARL_ENTRY_FLAG_FOUND && (do_hwcorrupt == CONFIG_BOOLEAN_YES || (do_hwcorrupt == CONFIG_BOOLEAN_AUTO && HardwareCorrupted > 0))) { + do_hwcorrupt = CONFIG_BOOLEAN_YES; - st = rrdset_find("mem.hwcorrupt"); + st = rrdset_find_localhost("mem.hwcorrupt"); if(!st) { - st = rrdset_create("mem", "hwcorrupt", NULL, "ecc", NULL, "Hardware Corrupted ECC", "MB", 9000, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("mem", "hwcorrupt", NULL, "ecc", NULL, "Hardware Corrupted ECC", "MB", 9000 + , update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "HardwareCorrupted", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(st, "HardwareCorrupted", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -201,12 +204,13 @@ int do_proc_meminfo(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_committed) { - st = rrdset_find("mem.committed"); + st = rrdset_find_localhost("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; + st = rrdset_create_localhost("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB" + , 5000, update_every, RRDSET_TYPE_AREA); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "Committed_AS", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(st, "Committed_AS", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -217,16 +221,17 @@ int do_proc_meminfo(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_writeback) { - st = rrdset_find("mem.writeback"); + st = rrdset_find_localhost("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); + st = rrdset_create_localhost("mem", "writeback", NULL, "kernel", NULL, "Writeback Memory", "MB", 4000 + , update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "Dirty", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "Writeback", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "FuseWriteback", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "NfsWriteback", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "Bounce", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -241,15 +246,16 @@ int do_proc_meminfo(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_kernel) { - st = rrdset_find("mem.kernel"); + st = rrdset_find_localhost("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); + st = rrdset_create_localhost("mem", "kernel", NULL, "kernel", NULL, "Memory Used by Kernel", "MB", 6000 + , update_every, RRDSET_TYPE_STACKED); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "Slab", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "KernelStack", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "PageTables", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "VmallocUsed", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -263,13 +269,14 @@ int do_proc_meminfo(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_slab) { - st = rrdset_find("mem.slab"); + st = rrdset_find_localhost("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; + st = rrdset_create_localhost("mem", "slab", NULL, "slab", NULL, "Reclaimable Kernel Memory", "MB", 6500 + , update_every, RRDSET_TYPE_STACKED); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "reclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE); - rrddim_add(st, "unreclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE); + rrddim_add(st, "reclaimable", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "unreclaimable", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); diff --git a/src/proc_net_dev.c b/src/proc_net_dev.c index 82661abd4..1b00758d5 100644 --- a/src/proc_net_dev.c +++ b/src/proc_net_dev.c @@ -8,6 +8,7 @@ struct netdev { // flags int configured; int enabled; + int updated; int do_bandwidth; int do_packets; @@ -18,23 +19,23 @@ struct netdev { int do_events; // data collected - unsigned long long rbytes; - unsigned long long rpackets; - unsigned long long rerrors; - unsigned long long rdrops; - unsigned long long rfifo; - unsigned long long rframe; - unsigned long long rcompressed; - unsigned long long rmulticast; - - unsigned long long tbytes; - unsigned long long tpackets; - unsigned long long terrors; - unsigned long long tdrops; - unsigned long long tfifo; - unsigned long long tcollisions; - unsigned long long tcarrier; - unsigned long long tcompressed; + kernel_uint_t rbytes; + kernel_uint_t rpackets; + kernel_uint_t rerrors; + kernel_uint_t rdrops; + kernel_uint_t rfifo; + kernel_uint_t rframe; + kernel_uint_t rcompressed; + kernel_uint_t rmulticast; + + kernel_uint_t tbytes; + kernel_uint_t tpackets; + kernel_uint_t terrors; + kernel_uint_t tdrops; + kernel_uint_t tfifo; + kernel_uint_t tcollisions; + kernel_uint_t tcarrier; + kernel_uint_t tcompressed; // charts RRDSET *st_bandwidth; @@ -67,26 +68,73 @@ struct netdev { struct netdev *next; }; -static struct netdev *netdev_root = NULL; +static struct netdev *netdev_root = NULL, *netdev_last_used = NULL; + +static size_t netdev_added = 0, netdev_found = 0; + +static void netdev_free(struct netdev *d) { + if(d->st_bandwidth) rrdset_flag_set(d->st_bandwidth, RRDSET_FLAG_OBSOLETE); + if(d->st_packets) rrdset_flag_set(d->st_packets, RRDSET_FLAG_OBSOLETE); + if(d->st_errors) rrdset_flag_set(d->st_errors, RRDSET_FLAG_OBSOLETE); + if(d->st_drops) rrdset_flag_set(d->st_drops, RRDSET_FLAG_OBSOLETE); + if(d->st_fifo) rrdset_flag_set(d->st_fifo, RRDSET_FLAG_OBSOLETE); + if(d->st_compressed) rrdset_flag_set(d->st_compressed, RRDSET_FLAG_OBSOLETE); + if(d->st_events) rrdset_flag_set(d->st_events, RRDSET_FLAG_OBSOLETE); + + netdev_added--; + freez(d->name); + freez(d); +} + +static void netdev_cleanup() { + if(likely(netdev_found == netdev_added)) return; + + netdev_added = 0; + struct netdev *d = netdev_root, *last = NULL; + while(d) { + if(unlikely(!d->updated)) { + // info("Removing network device '%s', linked after '%s'", d->name, last?last->name:"ROOT"); + + if(netdev_last_used == d) + netdev_last_used = last; + + struct netdev *t = d; + + if(d == netdev_root || !last) + netdev_root = d = d->next; + + else + last->next = d = d->next; + + t->next = NULL; + netdev_free(t); + } + else { + netdev_added++; + last = d; + d->updated = 0; + d = d->next; + } + } +} static struct netdev *get_netdev(const char *name) { - static struct netdev *last = NULL; struct netdev *d; uint32_t hash = simple_hash(name); // search it, from the last position to the end - for(d = last ; d ; d = d->next) { + for(d = netdev_last_used ; d ; d = d->next) { if(unlikely(hash == d->hash && !strcmp(name, d->name))) { - last = d->next; + netdev_last_used = d->next; return d; } } // search it from the beginning to the last position we used - for(d = netdev_root ; d != last ; d = d->next) { + for(d = netdev_root ; d != netdev_last_used ; d = d->next) { if(unlikely(hash == d->hash && !strcmp(name, d->name))) { - last = d->next; + netdev_last_used = d->next; return d; } } @@ -96,6 +144,7 @@ static struct netdev *get_netdev(const char *name) { d->name = strdupz(name); d->hash = simple_hash(d->name); d->len = strlen(d->name); + netdev_added++; // link it to the end if(netdev_root) { @@ -111,22 +160,21 @@ static struct netdev *get_netdev(const char *name) { int do_proc_net_dev(int update_every, usec_t dt) { (void)dt; - static SIMPLE_PATTERN *disabled_list = NULL; static procfile *ff = NULL; static int enable_new_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(unlikely(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); + enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_BOOLEAN_AUTO); - do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - do_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - do_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - do_drops = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - do_fifo = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - do_compressed = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - do_events = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_ONDEMAND_ONDEMAND); + do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_BOOLEAN_AUTO); + do_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_BOOLEAN_AUTO); + do_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_BOOLEAN_AUTO); + do_drops = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_BOOLEAN_AUTO); + do_fifo = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_BOOLEAN_AUTO); + do_compressed = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_BOOLEAN_AUTO); + do_events = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_BOOLEAN_AUTO); disabled_list = simple_pattern_create( config_get("plugin:proc:/proc/net/dev", "disable by default interfaces matching", "lo fireqos* *-ifb") @@ -135,7 +183,7 @@ int do_proc_net_dev(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/dev"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/dev"); ff = procfile_open(config_get("plugin:proc:/proc/net/dev", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; } @@ -143,12 +191,16 @@ int do_proc_net_dev(int update_every, usec_t dt) { ff = procfile_readall(ff); if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time + netdev_found = 0; + size_t lines = procfile_lines(ff), l; for(l = 2; l < lines ;l++) { // require 17 words on each line if(unlikely(procfile_linewords(ff, l) < 17)) continue; struct netdev *d = get_netdev(procfile_lineword(ff, l, 0)); + d->updated = 1; + netdev_found++; if(unlikely(!d->configured)) { // this is the first time we see this interface @@ -165,207 +217,293 @@ int do_proc_net_dev(int update_every, usec_t dt) { snprintfz(var_name, 512, "plugin:proc:/proc/net/dev:%s", d->name); d->enabled = config_get_boolean_ondemand(var_name, "enabled", d->enabled); - if(d->enabled == CONFIG_ONDEMAND_NO) + if(d->enabled == CONFIG_BOOLEAN_NO) continue; - d->do_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", do_bandwidth); - d->do_packets = config_get_boolean_ondemand(var_name, "packets", do_packets); - d->do_errors = config_get_boolean_ondemand(var_name, "errors", do_errors); - d->do_drops = config_get_boolean_ondemand(var_name, "drops", do_drops); - d->do_fifo = config_get_boolean_ondemand(var_name, "fifo", do_fifo); + d->do_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", do_bandwidth); + d->do_packets = config_get_boolean_ondemand(var_name, "packets", do_packets); + d->do_errors = config_get_boolean_ondemand(var_name, "errors", do_errors); + d->do_drops = config_get_boolean_ondemand(var_name, "drops", do_drops); + d->do_fifo = config_get_boolean_ondemand(var_name, "fifo", do_fifo); d->do_compressed = config_get_boolean_ondemand(var_name, "compressed", do_compressed); - d->do_events = config_get_boolean_ondemand(var_name, "events", do_events); + d->do_events = config_get_boolean_ondemand(var_name, "events", do_events); } if(unlikely(!d->enabled)) continue; - d->rbytes = str2ull(procfile_lineword(ff, l, 1)); - d->rpackets = str2ull(procfile_lineword(ff, l, 2)); - d->rerrors = str2ull(procfile_lineword(ff, l, 3)); - d->rdrops = str2ull(procfile_lineword(ff, l, 4)); - d->rfifo = str2ull(procfile_lineword(ff, l, 5)); - d->rframe = str2ull(procfile_lineword(ff, l, 6)); - d->rcompressed = str2ull(procfile_lineword(ff, l, 7)); - d->rmulticast = str2ull(procfile_lineword(ff, l, 8)); - - d->tbytes = str2ull(procfile_lineword(ff, l, 9)); - d->tpackets = str2ull(procfile_lineword(ff, l, 10)); - d->terrors = str2ull(procfile_lineword(ff, l, 11)); - d->tdrops = str2ull(procfile_lineword(ff, l, 12)); - d->tfifo = str2ull(procfile_lineword(ff, l, 13)); - d->tcollisions = str2ull(procfile_lineword(ff, l, 14)); - d->tcarrier = str2ull(procfile_lineword(ff, l, 15)); - d->tcompressed = str2ull(procfile_lineword(ff, l, 16)); + if(likely(d->do_bandwidth != CONFIG_BOOLEAN_NO)) { + d->rbytes = str2kernel_uint_t(procfile_lineword(ff, l, 1)); + d->tbytes = str2kernel_uint_t(procfile_lineword(ff, l, 9)); + } + + if(likely(d->do_packets != CONFIG_BOOLEAN_NO)) { + d->rpackets = str2kernel_uint_t(procfile_lineword(ff, l, 2)); + d->rmulticast = str2kernel_uint_t(procfile_lineword(ff, l, 8)); + d->tpackets = str2kernel_uint_t(procfile_lineword(ff, l, 10)); + } + + if(likely(d->do_errors != CONFIG_BOOLEAN_NO)) { + d->rerrors = str2kernel_uint_t(procfile_lineword(ff, l, 3)); + d->terrors = str2kernel_uint_t(procfile_lineword(ff, l, 11)); + } + + if(likely(d->do_drops != CONFIG_BOOLEAN_NO)) { + d->rdrops = str2kernel_uint_t(procfile_lineword(ff, l, 4)); + d->tdrops = str2kernel_uint_t(procfile_lineword(ff, l, 12)); + } + + if(likely(d->do_fifo != CONFIG_BOOLEAN_NO)) { + d->rfifo = str2kernel_uint_t(procfile_lineword(ff, l, 5)); + d->tfifo = str2kernel_uint_t(procfile_lineword(ff, l, 13)); + } + + if(likely(d->do_compressed != CONFIG_BOOLEAN_NO)) { + d->rcompressed = str2kernel_uint_t(procfile_lineword(ff, l, 7)); + d->tcompressed = str2kernel_uint_t(procfile_lineword(ff, l, 16)); + } + + if(likely(d->do_events != CONFIG_BOOLEAN_NO)) { + d->rframe = str2kernel_uint_t(procfile_lineword(ff, l, 6)); + d->tcollisions = str2kernel_uint_t(procfile_lineword(ff, l, 14)); + d->tcarrier = str2kernel_uint_t(procfile_lineword(ff, l, 15)); + } // -------------------------------------------------------------------- - if(unlikely((d->do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (d->rbytes || d->tbytes)))) - d->do_bandwidth = CONFIG_ONDEMAND_YES; + if(unlikely((d->do_bandwidth == CONFIG_BOOLEAN_AUTO && (d->rbytes || d->tbytes)))) + d->do_bandwidth = CONFIG_BOOLEAN_YES; - if(d->do_bandwidth == CONFIG_ONDEMAND_YES) { + if(d->do_bandwidth == CONFIG_BOOLEAN_YES) { if(unlikely(!d->st_bandwidth)) { - d->st_bandwidth = rrdset_find_bytype("net", d->name); - if(!d->st_bandwidth) - d->st_bandwidth = rrdset_create("net", d->name, NULL, d->name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA); - - d->rd_rbytes = rrddim_add(d->st_bandwidth, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); - d->rd_tbytes = rrddim_add(d->st_bandwidth, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); + d->st_bandwidth = rrdset_create_localhost( + "net" + , d->name + , NULL + , d->name + , "net.net" + , "Bandwidth" + , "kilobits/s" + , 7000 + , update_every + , RRDSET_TYPE_AREA + ); + + d->rd_rbytes = rrddim_add(d->st_bandwidth, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + d->rd_tbytes = rrddim_add(d->st_bandwidth, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(d->st_bandwidth); - rrddim_set_by_pointer(d->st_bandwidth, d->rd_rbytes, d->rbytes); - rrddim_set_by_pointer(d->st_bandwidth, d->rd_tbytes, d->tbytes); + rrddim_set_by_pointer(d->st_bandwidth, d->rd_rbytes, (collected_number)d->rbytes); + rrddim_set_by_pointer(d->st_bandwidth, d->rd_tbytes, (collected_number)d->tbytes); rrdset_done(d->st_bandwidth); } // -------------------------------------------------------------------- - if(unlikely((d->do_packets == CONFIG_ONDEMAND_ONDEMAND && (d->rpackets || d->tpackets || d->rmulticast)))) - d->do_packets = CONFIG_ONDEMAND_YES; + if(unlikely((d->do_packets == CONFIG_BOOLEAN_AUTO && (d->rpackets || d->tpackets || d->rmulticast)))) + d->do_packets = CONFIG_BOOLEAN_YES; - if(d->do_packets == CONFIG_ONDEMAND_YES) { + if(d->do_packets == CONFIG_BOOLEAN_YES) { if(unlikely(!d->st_packets)) { - d->st_packets = rrdset_find_bytype("net_packets", d->name); - - if(!d->st_packets) - d->st_packets = rrdset_create("net_packets", d->name, NULL, d->name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE); - d->st_packets->isdetail = 1; - - d->rd_rpackets = rrddim_add(d->st_packets, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); - d->rd_tpackets = rrddim_add(d->st_packets, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); - d->rd_rmulticast = rrddim_add(d->st_packets, "multicast", NULL, 1, 1, RRDDIM_INCREMENTAL); + d->st_packets = rrdset_create_localhost( + "net_packets" + , d->name + , NULL + , d->name + , "net.packets" + , "Packets" + , "packets/s" + , 7001 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_packets, RRDSET_FLAG_DETAIL); + + d->rd_rpackets = rrddim_add(d->st_packets, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + d->rd_tpackets = rrddim_add(d->st_packets, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + d->rd_rmulticast = rrddim_add(d->st_packets, "multicast", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(d->st_packets); - rrddim_set_by_pointer(d->st_packets, d->rd_rpackets, d->rpackets); - rrddim_set_by_pointer(d->st_packets, d->rd_tpackets, d->tpackets); - rrddim_set_by_pointer(d->st_packets, d->rd_rmulticast, d->rmulticast); + rrddim_set_by_pointer(d->st_packets, d->rd_rpackets, (collected_number)d->rpackets); + rrddim_set_by_pointer(d->st_packets, d->rd_tpackets, (collected_number)d->tpackets); + rrddim_set_by_pointer(d->st_packets, d->rd_rmulticast, (collected_number)d->rmulticast); rrdset_done(d->st_packets); } // -------------------------------------------------------------------- - if(unlikely((d->do_errors == CONFIG_ONDEMAND_ONDEMAND && (d->rerrors || d->terrors)))) - d->do_errors = CONFIG_ONDEMAND_YES; + if(unlikely((d->do_errors == CONFIG_BOOLEAN_AUTO && (d->rerrors || d->terrors)))) + d->do_errors = CONFIG_BOOLEAN_YES; - if(d->do_errors == CONFIG_ONDEMAND_YES) { + if(d->do_errors == CONFIG_BOOLEAN_YES) { if(unlikely(!d->st_errors)) { - d->st_errors = rrdset_find_bytype("net_errors", d->name); - - if(!d->st_errors) - d->st_errors = rrdset_create("net_errors", d->name, NULL, d->name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE); - - d->st_errors->isdetail = 1; - d->rd_rerrors = rrddim_add(d->st_errors, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); - d->rd_terrors = rrddim_add(d->st_errors, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); + d->st_errors = rrdset_create_localhost( + "net_errors" + , d->name + , NULL + , d->name + , "net.errors" + , "Interface Errors" + , "errors/s" + , 7002 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_errors, RRDSET_FLAG_DETAIL); + + d->rd_rerrors = rrddim_add(d->st_errors, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + d->rd_terrors = rrddim_add(d->st_errors, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(d->st_errors); - rrddim_set_by_pointer(d->st_errors, d->rd_rerrors, d->rerrors); - rrddim_set_by_pointer(d->st_errors, d->rd_terrors, d->terrors); + rrddim_set_by_pointer(d->st_errors, d->rd_rerrors, (collected_number)d->rerrors); + rrddim_set_by_pointer(d->st_errors, d->rd_terrors, (collected_number)d->terrors); rrdset_done(d->st_errors); } // -------------------------------------------------------------------- - if(unlikely((d->do_drops == CONFIG_ONDEMAND_ONDEMAND && (d->rdrops || d->tdrops)))) - d->do_drops = CONFIG_ONDEMAND_YES; + if(unlikely((d->do_drops == CONFIG_BOOLEAN_AUTO && (d->rdrops || d->tdrops)))) + d->do_drops = CONFIG_BOOLEAN_YES; - if(d->do_drops == CONFIG_ONDEMAND_YES) { + if(d->do_drops == CONFIG_BOOLEAN_YES) { if(unlikely(!d->st_drops)) { - d->st_drops = rrdset_find_bytype("net_drops", d->name); - - if(!d->st_drops) - d->st_drops = rrdset_create("net_drops", d->name, NULL, d->name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE); - - d->st_drops->isdetail = 1; - d->rd_rdrops = rrddim_add(d->st_drops, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); - d->rd_tdrops = rrddim_add(d->st_drops, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); + d->st_drops = rrdset_create_localhost( + "net_drops" + , d->name + , NULL + , d->name + , "net.drops" + , "Interface Drops" + , "drops/s" + , 7003 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_drops, RRDSET_FLAG_DETAIL); + + d->rd_rdrops = rrddim_add(d->st_drops, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + d->rd_tdrops = rrddim_add(d->st_drops, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(d->st_drops); - rrddim_set_by_pointer(d->st_drops, d->rd_rdrops, d->rdrops); - rrddim_set_by_pointer(d->st_drops, d->rd_tdrops, d->tdrops); + rrddim_set_by_pointer(d->st_drops, d->rd_rdrops, (collected_number)d->rdrops); + rrddim_set_by_pointer(d->st_drops, d->rd_tdrops, (collected_number)d->tdrops); rrdset_done(d->st_drops); } // -------------------------------------------------------------------- - if(unlikely((d->do_fifo == CONFIG_ONDEMAND_ONDEMAND && (d->rfifo || d->tfifo)))) - d->do_fifo = CONFIG_ONDEMAND_YES; + if(unlikely((d->do_fifo == CONFIG_BOOLEAN_AUTO && (d->rfifo || d->tfifo)))) + d->do_fifo = CONFIG_BOOLEAN_YES; - if(d->do_fifo == CONFIG_ONDEMAND_YES) { + if(d->do_fifo == CONFIG_BOOLEAN_YES) { if(unlikely(!d->st_fifo)) { - d->st_fifo = rrdset_find_bytype("net_fifo", d->name); - if(!d->st_fifo) - d->st_fifo = rrdset_create("net_fifo", d->name, NULL, d->name, "net.fifo", "Interface FIFO Buffer Errors", "errors", 7004, update_every, RRDSET_TYPE_LINE); - - d->st_fifo->isdetail = 1; - - d->rd_rfifo = rrddim_add(d->st_fifo, "receive", NULL, 1, 1, RRDDIM_INCREMENTAL); - d->rd_tfifo = rrddim_add(d->st_fifo, "transmit", NULL, -1, 1, RRDDIM_INCREMENTAL); + d->st_fifo = rrdset_create_localhost( + "net_fifo" + , d->name + , NULL + , d->name + , "net.fifo" + , "Interface FIFO Buffer Errors" + , "errors" + , 7004 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_fifo, RRDSET_FLAG_DETAIL); + + d->rd_rfifo = rrddim_add(d->st_fifo, "receive", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + d->rd_tfifo = rrddim_add(d->st_fifo, "transmit", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(d->st_fifo); - rrddim_set_by_pointer(d->st_fifo, d->rd_rfifo, d->rfifo); - rrddim_set_by_pointer(d->st_fifo, d->rd_tfifo, d->tfifo); + rrddim_set_by_pointer(d->st_fifo, d->rd_rfifo, (collected_number)d->rfifo); + rrddim_set_by_pointer(d->st_fifo, d->rd_tfifo, (collected_number)d->tfifo); rrdset_done(d->st_fifo); } // -------------------------------------------------------------------- - if(unlikely((d->do_compressed == CONFIG_ONDEMAND_ONDEMAND && (d->rcompressed || d->tcompressed)))) - d->do_compressed = CONFIG_ONDEMAND_YES; + if(unlikely((d->do_compressed == CONFIG_BOOLEAN_AUTO && (d->rcompressed || d->tcompressed)))) + d->do_compressed = CONFIG_BOOLEAN_YES; - if(d->do_compressed == CONFIG_ONDEMAND_YES) { + if(d->do_compressed == CONFIG_BOOLEAN_YES) { if(unlikely(!d->st_compressed)) { - d->st_compressed = rrdset_find_bytype("net_compressed", d->name); - if(!d->st_compressed) - d->st_compressed = rrdset_create("net_compressed", d->name, NULL, d->name, "net.compressed", "Compressed Packets", "packets/s", 7005, update_every, RRDSET_TYPE_LINE); - - d->st_compressed->isdetail = 1; - d->rd_rcompressed = rrddim_add(d->st_compressed, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); - d->rd_tcompressed = rrddim_add(d->st_compressed, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + d->st_compressed = rrdset_create_localhost( + "net_compressed" + , d->name + , NULL + , d->name + , "net.compressed" + , "Compressed Packets" + , "packets/s" + , 7005 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_compressed, RRDSET_FLAG_DETAIL); + + d->rd_rcompressed = rrddim_add(d->st_compressed, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + d->rd_tcompressed = rrddim_add(d->st_compressed, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(d->st_compressed); - rrddim_set_by_pointer(d->st_compressed, d->rd_rcompressed, d->rcompressed); - rrddim_set_by_pointer(d->st_compressed, d->rd_tcompressed, d->tcompressed); + rrddim_set_by_pointer(d->st_compressed, d->rd_rcompressed, (collected_number)d->rcompressed); + rrddim_set_by_pointer(d->st_compressed, d->rd_tcompressed, (collected_number)d->tcompressed); rrdset_done(d->st_compressed); } // -------------------------------------------------------------------- - if(unlikely((d->do_events == CONFIG_ONDEMAND_ONDEMAND && (d->rframe || d->tcollisions || d->tcarrier)))) - d->do_events = CONFIG_ONDEMAND_YES; + if(unlikely((d->do_events == CONFIG_BOOLEAN_AUTO && (d->rframe || d->tcollisions || d->tcarrier)))) + d->do_events = CONFIG_BOOLEAN_YES; - if(d->do_events == CONFIG_ONDEMAND_YES) { + if(d->do_events == CONFIG_BOOLEAN_YES) { if(unlikely(!d->st_events)) { - d->st_events = rrdset_find_bytype("net_events", d->name); - if(!d->st_events) - d->st_events = rrdset_create("net_events", d->name, NULL, d->name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE); - d->st_events->isdetail = 1; - - d->rd_rframe = rrddim_add(d->st_events, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL); - d->rd_tcollisions = rrddim_add(d->st_events, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL); - d->rd_tcarrier = rrddim_add(d->st_events, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL); + d->st_events = rrdset_create_localhost( + "net_events" + , d->name + , NULL + , d->name + , "net.events" + , "Network Interface Events" + , "events/s" + , 7006 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(d->st_events, RRDSET_FLAG_DETAIL); + + d->rd_rframe = rrddim_add(d->st_events, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + d->rd_tcollisions = rrddim_add(d->st_events, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + d->rd_tcarrier = rrddim_add(d->st_events, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(d->st_events); - rrddim_set_by_pointer(d->st_events, d->rd_rframe, d->rframe); - rrddim_set_by_pointer(d->st_events, d->rd_tcollisions, d->tcollisions); - rrddim_set_by_pointer(d->st_events, d->rd_tcarrier, d->tcarrier); + rrddim_set_by_pointer(d->st_events, d->rd_rframe, (collected_number)d->rframe); + rrddim_set_by_pointer(d->st_events, d->rd_tcollisions, (collected_number)d->tcollisions); + rrddim_set_by_pointer(d->st_events, d->rd_tcarrier, (collected_number)d->tcarrier); rrdset_done(d->st_events); } } + netdev_cleanup(); + return 0; } diff --git a/src/proc_net_ip_vs_stats.c b/src/proc_net_ip_vs_stats.c index 34cadaea7..16a3234df 100644 --- a/src/proc_net_ip_vs_stats.c +++ b/src/proc_net_ip_vs_stats.c @@ -3,6 +3,7 @@ #define RRD_TYPE_NET_IPVS "ipvs" int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { + (void)dt; static int do_bandwidth = -1, do_sockets = -1, do_packets = -1; static procfile *ff = NULL; @@ -10,11 +11,9 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { 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(!ff) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/ip_vs_stats"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_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; @@ -41,11 +40,13 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_sockets) { - st = rrdset_find(RRD_TYPE_NET_IPVS ".sockets"); + st = rrdset_find_localhost(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", 3101, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, NULL + , "IPVS New Connections", "connections/s", 3101, update_every + , RRDSET_TYPE_LINE); - rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "connections", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -56,12 +57,13 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_packets) { - st = rrdset_find(RRD_TYPE_NET_IPVS ".packets"); + st = rrdset_find_localhost(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", 3102, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Packets" + , "packets/s", 3102, 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, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -73,12 +75,13 @@ int do_proc_net_ip_vs_stats(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_bandwidth) { - st = rrdset_find(RRD_TYPE_NET_IPVS ".net"); + st = rrdset_find_localhost(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", 3100, update_every, RRDSET_TYPE_AREA); + st = rrdset_create_localhost(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Bandwidth" + , "kilobits/s", 3100, update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/proc_net_netstat.c b/src/proc_net_netstat.c index 8741a71c9..2677a6c17 100644 --- a/src/proc_net_netstat.c +++ b/src/proc_net_netstat.c @@ -98,19 +98,19 @@ int do_proc_net_netstat(int update_every, usec_t dt) { hash_ipext = simple_hash("IpExt"); hash_tcpext = simple_hash("TcpExt"); - do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_ONDEMAND_ONDEMAND); - do_inerrors = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_ONDEMAND_ONDEMAND); - do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND); - do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND); - do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_ONDEMAND_ONDEMAND); - do_bcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_ONDEMAND_ONDEMAND); - do_ecn = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "ECN packets", CONFIG_ONDEMAND_ONDEMAND); - - do_tcpext_reorder = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP reorders", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_syscookies = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_ofo = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_connaborts = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND); - do_tcpext_memory = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP memory pressures", CONFIG_ONDEMAND_ONDEMAND); + do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_BOOLEAN_AUTO); + do_inerrors = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_BOOLEAN_AUTO); + do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_BOOLEAN_AUTO); + do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_BOOLEAN_AUTO); + do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_BOOLEAN_AUTO); + do_bcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_BOOLEAN_AUTO); + do_ecn = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "ECN packets", CONFIG_BOOLEAN_AUTO); + + do_tcpext_reorder = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP reorders", CONFIG_BOOLEAN_AUTO); + do_tcpext_syscookies = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP SYN cookies", CONFIG_BOOLEAN_AUTO); + do_tcpext_ofo = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP out-of-order queue", CONFIG_BOOLEAN_AUTO); + do_tcpext_connaborts = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP connection aborts", CONFIG_BOOLEAN_AUTO); + do_tcpext_memory = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP memory pressures", CONFIG_BOOLEAN_AUTO); arl_ipext = arl_create("netstat/ipext", NULL, 60); arl_tcpext = arl_create("netstat/tcpext", NULL, 60); @@ -118,38 +118,38 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- // IPv4 - if(do_bandwidth != CONFIG_ONDEMAND_NO) { + if(do_bandwidth != CONFIG_BOOLEAN_NO) { arl_expect(arl_ipext, "InOctets", &ipext_InOctets); arl_expect(arl_ipext, "OutOctets", &ipext_OutOctets); } - if(do_inerrors != CONFIG_ONDEMAND_NO) { + if(do_inerrors != CONFIG_BOOLEAN_NO) { arl_expect(arl_ipext, "InNoRoutes", &ipext_InNoRoutes); arl_expect(arl_ipext, "InTruncatedPkts", &ipext_InTruncatedPkts); arl_expect(arl_ipext, "InCsumErrors", &ipext_InCsumErrors); } - if(do_mcast != CONFIG_ONDEMAND_NO) { + if(do_mcast != CONFIG_BOOLEAN_NO) { arl_expect(arl_ipext, "InMcastOctets", &ipext_InMcastOctets); arl_expect(arl_ipext, "OutMcastOctets", &ipext_OutMcastOctets); } - if(do_mcast_p != CONFIG_ONDEMAND_NO) { + if(do_mcast_p != CONFIG_BOOLEAN_NO) { arl_expect(arl_ipext, "InMcastPkts", &ipext_InMcastPkts); arl_expect(arl_ipext, "OutMcastPkts", &ipext_OutMcastPkts); } - if(do_bcast != CONFIG_ONDEMAND_NO) { + if(do_bcast != CONFIG_BOOLEAN_NO) { arl_expect(arl_ipext, "InBcastPkts", &ipext_InBcastPkts); arl_expect(arl_ipext, "OutBcastPkts", &ipext_OutBcastPkts); } - if(do_bcast_p != CONFIG_ONDEMAND_NO) { + if(do_bcast_p != CONFIG_BOOLEAN_NO) { arl_expect(arl_ipext, "InBcastOctets", &ipext_InBcastOctets); arl_expect(arl_ipext, "OutBcastOctets", &ipext_OutBcastOctets); } - if(do_ecn != CONFIG_ONDEMAND_NO) { + if(do_ecn != CONFIG_BOOLEAN_NO) { arl_expect(arl_ipext, "InNoECTPkts", &ipext_InNoECTPkts); arl_expect(arl_ipext, "InECT1Pkts", &ipext_InECT1Pkts); arl_expect(arl_ipext, "InECT0Pkts", &ipext_InECT0Pkts); @@ -159,27 +159,27 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- // IPv4 TCP - if(do_tcpext_reorder != CONFIG_ONDEMAND_NO) { + if(do_tcpext_reorder != CONFIG_BOOLEAN_NO) { arl_expect(arl_tcpext, "TCPFACKReorder", &tcpext_TCPFACKReorder); arl_expect(arl_tcpext, "TCPSACKReorder", &tcpext_TCPSACKReorder); arl_expect(arl_tcpext, "TCPRenoReorder", &tcpext_TCPRenoReorder); arl_expect(arl_tcpext, "TCPTSReorder", &tcpext_TCPTSReorder); } - if(do_tcpext_syscookies != CONFIG_ONDEMAND_NO) { + if(do_tcpext_syscookies != CONFIG_BOOLEAN_NO) { arl_expect(arl_tcpext, "SyncookiesSent", &tcpext_SyncookiesSent); arl_expect(arl_tcpext, "SyncookiesRecv", &tcpext_SyncookiesRecv); arl_expect(arl_tcpext, "SyncookiesFailed", &tcpext_SyncookiesFailed); } - if(do_tcpext_ofo != CONFIG_ONDEMAND_NO) { + if(do_tcpext_ofo != CONFIG_BOOLEAN_NO) { arl_expect(arl_tcpext, "TCPOFOQueue", &tcpext_TCPOFOQueue); arl_expect(arl_tcpext, "TCPOFODrop", &tcpext_TCPOFODrop); arl_expect(arl_tcpext, "TCPOFOMerge", &tcpext_TCPOFOMerge); arl_expect(arl_tcpext, "OfoPruned", &tcpext_OfoPruned); } - if(do_tcpext_connaborts != CONFIG_ONDEMAND_NO) { + if(do_tcpext_connaborts != CONFIG_BOOLEAN_NO) { arl_expect(arl_tcpext, "TCPAbortOnData", &tcpext_TCPAbortOnData); arl_expect(arl_tcpext, "TCPAbortOnClose", &tcpext_TCPAbortOnClose); arl_expect(arl_tcpext, "TCPAbortOnMemory", &tcpext_TCPAbortOnMemory); @@ -188,14 +188,14 @@ int do_proc_net_netstat(int update_every, usec_t dt) { arl_expect(arl_tcpext, "TCPAbortFailed", &tcpext_TCPAbortFailed); } - if(do_tcpext_memory != CONFIG_ONDEMAND_NO) { + if(do_tcpext_memory != CONFIG_BOOLEAN_NO) { arl_expect(arl_tcpext, "TCPMemoryPressures", &tcpext_TCPMemoryPressures); } } if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/netstat"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/netstat"); ff = procfile_open(config_get("plugin:proc:/proc/net/netstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; } @@ -206,6 +206,9 @@ int do_proc_net_netstat(int update_every, usec_t dt) { size_t lines = procfile_lines(ff), l; size_t words; + arl_begin(arl_ipext); + arl_begin(arl_tcpext); + for(l = 0; l < lines ;l++) { char *key = procfile_lineword(ff, l, 0); uint32_t hash = simple_hash(key); @@ -219,21 +222,21 @@ int do_proc_net_netstat(int update_every, usec_t dt) { continue; } - arl_begin(arl_ipext); parse_line_pair(ff, arl_ipext, h, l); RRDSET *st; // -------------------------------------------------------------------- - if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (ipext_InOctets || ipext_OutOctets))) { - do_bandwidth = CONFIG_ONDEMAND_YES; - st = rrdset_find("system.ipv4"); + if(do_bandwidth == CONFIG_BOOLEAN_YES || (do_bandwidth == CONFIG_BOOLEAN_AUTO && (ipext_InOctets || ipext_OutOctets))) { + do_bandwidth = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("system.ipv4"); if(unlikely(!st)) { - st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA); + st = rrdset_create_localhost("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s" + , 500, update_every, RRDSET_TYPE_AREA); - rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "InOctets", "received", 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutOctets", "sent", -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -244,16 +247,17 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (ipext_InNoRoutes || ipext_InTruncatedPkts))) { - do_inerrors = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.inerrors"); + if(do_inerrors == CONFIG_BOOLEAN_YES || (do_inerrors == CONFIG_BOOLEAN_AUTO && (ipext_InNoRoutes || ipext_InTruncatedPkts))) { + do_inerrors = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.inerrors"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors", "packets/s", 4000, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors" + , "packets/s", 4000, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InNoRoutes", "noroutes", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InTruncatedPkts", "truncated", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InCsumErrors", "checksum", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InNoRoutes", "noroutes", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InTruncatedPkts", "truncated", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", "checksum", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -265,15 +269,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (ipext_InMcastOctets || ipext_OutMcastOctets))) { - do_mcast = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.mcast"); + if(do_mcast == CONFIG_BOOLEAN_YES || (do_mcast == CONFIG_BOOLEAN_AUTO && (ipext_InMcastOctets || ipext_OutMcastOctets))) { + do_mcast = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.mcast"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA); - st->isdetail = 1; + st = rrdset_create_localhost("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth" + , "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InMcastOctets", "received", 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutMcastOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "InMcastOctets", "received", 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutMcastOctets", "sent", -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -284,15 +289,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (ipext_InBcastOctets || ipext_OutBcastOctets))) { - do_bcast = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.bcast"); + if(do_bcast == CONFIG_BOOLEAN_YES || (do_bcast == CONFIG_BOOLEAN_AUTO && (ipext_InBcastOctets || ipext_OutBcastOctets))) { + do_bcast = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.bcast"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA); - st->isdetail = 1; + st = rrdset_create_localhost("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth" + , "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InBcastOctets", "received", 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutBcastOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "InBcastOctets", "received", 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutBcastOctets", "sent", -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -303,15 +309,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (ipext_InMcastPkts || ipext_OutMcastPkts))) { - do_mcast_p = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.mcastpkts"); + if(do_mcast_p == CONFIG_BOOLEAN_YES || (do_mcast_p == CONFIG_BOOLEAN_AUTO && (ipext_InMcastPkts || ipext_OutMcastPkts))) { + do_mcast_p = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.mcastpkts"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets", "packets/s", 8600, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets" + , "packets/s", 8600, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InMcastPkts", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutMcastPkts", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InMcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutMcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -322,15 +329,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (ipext_InBcastPkts || ipext_OutBcastPkts))) { - do_bcast_p = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.bcastpkts"); + if(do_bcast_p == CONFIG_BOOLEAN_YES || (do_bcast_p == CONFIG_BOOLEAN_AUTO && (ipext_InBcastPkts || ipext_OutBcastPkts))) { + do_bcast_p = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.bcastpkts"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets", "packets/s", 8500, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets" + , "packets/s", 8500, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InBcastPkts", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutBcastPkts", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InBcastPkts", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutBcastPkts", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -341,17 +349,18 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (ipext_InCEPkts || ipext_InECT0Pkts || ipext_InECT1Pkts || ipext_InNoECTPkts))) { - do_ecn = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.ecnpkts"); + if(do_ecn == CONFIG_BOOLEAN_YES || (do_ecn == CONFIG_BOOLEAN_AUTO && (ipext_InCEPkts || ipext_InECT0Pkts || ipext_InECT1Pkts || ipext_InNoECTPkts))) { + do_ecn = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.ecnpkts"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; - - rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics" + , "packets/s", 8700, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -371,20 +380,20 @@ int do_proc_net_netstat(int update_every, usec_t dt) { continue; } - arl_begin(arl_tcpext); parse_line_pair(ff, arl_tcpext, h, l); RRDSET *st; // -------------------------------------------------------------------- - if(do_tcpext_memory == CONFIG_ONDEMAND_YES || (do_tcpext_memory == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPMemoryPressures))) { - do_tcpext_memory = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.tcpmemorypressures"); + if(do_tcpext_memory == CONFIG_BOOLEAN_YES || (do_tcpext_memory == CONFIG_BOOLEAN_AUTO && (tcpext_TCPMemoryPressures))) { + do_tcpext_memory = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.tcpmemorypressures"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "tcpmemorypressures", NULL, "tcp", NULL, "TCP Memory Pressures", "events/s", 3000, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "tcpmemorypressures", NULL, "tcp", NULL, "TCP Memory Pressures" + , "events/s", 3000, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "TCPMemoryPressures", "pressures", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPMemoryPressures", "pressures", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -394,18 +403,19 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPAbortOnData || tcpext_TCPAbortOnClose || tcpext_TCPAbortOnMemory || tcpext_TCPAbortOnTimeout || tcpext_TCPAbortOnLinger || tcpext_TCPAbortFailed))) { - do_tcpext_connaborts = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.tcpconnaborts"); + if(do_tcpext_connaborts == CONFIG_BOOLEAN_YES || (do_tcpext_connaborts == CONFIG_BOOLEAN_AUTO && (tcpext_TCPAbortOnData || tcpext_TCPAbortOnClose || tcpext_TCPAbortOnMemory || tcpext_TCPAbortOnTimeout || tcpext_TCPAbortOnLinger || tcpext_TCPAbortFailed))) { + do_tcpext_connaborts = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.tcpconnaborts"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnClose", "userclosed", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortOnLinger", "linger", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPAbortFailed", "failed", -1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts" + , "connections/s", 3010, update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnClose", "userclosed", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPAbortOnLinger", "linger", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPAbortFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -419,16 +429,18 @@ int do_proc_net_netstat(int update_every, usec_t dt) { } // -------------------------------------------------------------------- - if(do_tcpext_reorder == CONFIG_ONDEMAND_YES || (do_tcpext_reorder == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPRenoReorder || tcpext_TCPFACKReorder || tcpext_TCPSACKReorder || tcpext_TCPTSReorder))) { - do_tcpext_reorder = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.tcpreorders"); + if(do_tcpext_reorder == CONFIG_BOOLEAN_YES || (do_tcpext_reorder == CONFIG_BOOLEAN_AUTO && (tcpext_TCPRenoReorder || tcpext_TCPFACKReorder || tcpext_TCPSACKReorder || tcpext_TCPTSReorder))) { + do_tcpext_reorder = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.tcpreorders"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "tcpreorders", NULL, "tcp", NULL, "TCP Reordered Packets by Detection Method", "packets/s", 3020, update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "TCPTSReorder", "timestamp", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPSACKReorder", "sack", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPFACKReorder", "fack", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPRenoReorder", "reno", 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost("ipv4", "tcpreorders", NULL, "tcp", NULL + , "TCP Reordered Packets by Detection Method", "packets/s", 3020 + , update_every, RRDSET_TYPE_LINE); + + rrddim_add(st, "TCPTSReorder", "timestamp", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPSACKReorder", "sack", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPFACKReorder", "fack", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPRenoReorder", "reno", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -441,16 +453,17 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && (tcpext_TCPOFOQueue || tcpext_TCPOFODrop || tcpext_TCPOFOMerge))) { - do_tcpext_ofo = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.tcpofo"); + if(do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && (tcpext_TCPOFOQueue || tcpext_TCPOFODrop || tcpext_TCPOFOMerge))) { + do_tcpext_ofo = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.tcpofo"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue" + , "packets/s", 3050, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPOFODrop", "dropped", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "TCPOFOMerge", "merged", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OfoPruned", "pruned", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPOFODrop", "dropped", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "TCPOFOMerge", "merged", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OfoPruned", "pruned", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -463,15 +476,16 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (tcpext_SyncookiesSent || tcpext_SyncookiesRecv || tcpext_SyncookiesFailed))) { - do_tcpext_syscookies = CONFIG_ONDEMAND_YES; - st = rrdset_find("ipv4.tcpsyncookies"); + if(do_tcpext_syscookies == CONFIG_BOOLEAN_YES || (do_tcpext_syscookies == CONFIG_BOOLEAN_AUTO && (tcpext_SyncookiesSent || tcpext_SyncookiesRecv || tcpext_SyncookiesFailed))) { + do_tcpext_syscookies = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("ipv4.tcpsyncookies"); if(unlikely(!st)) { - st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies" + , "packets/s", 3100, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/proc_net_rpc_nfs.c b/src/proc_net_rpc_nfs.c index 9dba08d56..0df919635 100644 --- a/src/proc_net_rpc_nfs.c +++ b/src/proc_net_rpc_nfs.c @@ -136,7 +136,7 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { if(!ff) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/rpc/nfs"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/rpc/nfs"); ff = procfile_open(config_get("plugin:proc:/proc/net/rpc/nfs", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); } if(!ff) return 1; @@ -269,13 +269,14 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_net == 2) { - st = rrdset_find_bytype("nfs", "net"); + st = rrdset_find_bytype_localhost("nfs", "net"); if(!st) { - st = rrdset_create("nfs", "net", NULL, "network", NULL, "NFS Client Network", "operations/s", 5007, update_every, RRDSET_TYPE_STACKED); - st->isdetail = 1; + st = rrdset_create_localhost("nfs", "net", NULL, "network", NULL, "NFS Client Network", "operations/s", 5007 + , update_every, RRDSET_TYPE_STACKED); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "tcp", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -291,14 +292,15 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_rpc == 2) { - st = rrdset_find_bytype("nfs", "rpc"); + st = rrdset_find_bytype_localhost("nfs", "rpc"); if(!st) { - st = rrdset_create("nfs", "rpc", NULL, "rpc", NULL, "NFS Client Remote Procedure Calls Statistics", "calls/s", 5008, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + st = rrdset_create_localhost("nfs", "rpc", NULL, "rpc", NULL, "NFS Client Remote Procedure Calls Statistics" + , "calls/s", 5008, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "calls", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "retransmits", NULL, -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "auth_refresh", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "calls", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "retransmits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "auth_refresh", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -312,12 +314,13 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { if(do_proc2 == 2) { unsigned int i; - st = rrdset_find_bytype("nfs", "proc2"); + st = rrdset_find_bytype_localhost("nfs", "proc2"); if(!st) { - st = rrdset_create("nfs", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Client Remote Procedure Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("nfs", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Client Remote Procedure Calls" + , "calls/s", 5009, update_every, RRDSET_TYPE_STACKED); for(i = 0; nfs_proc2_values[i].present ; i++) - rrddim_add(st, nfs_proc2_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, nfs_proc2_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -331,12 +334,13 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { if(do_proc3 == 2) { unsigned int i; - st = rrdset_find_bytype("nfs", "proc3"); + st = rrdset_find_bytype_localhost("nfs", "proc3"); if(!st) { - st = rrdset_create("nfs", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Client Remote Procedure Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("nfs", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Client Remote Procedure Calls" + , "calls/s", 5010, update_every, RRDSET_TYPE_STACKED); for(i = 0; nfs_proc3_values[i].present ; i++) - rrddim_add(st, nfs_proc3_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, nfs_proc3_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -350,12 +354,13 @@ int do_proc_net_rpc_nfs(int update_every, usec_t dt) { if(do_proc4 == 2) { unsigned int i; - st = rrdset_find_bytype("nfs", "proc4"); + st = rrdset_find_bytype_localhost("nfs", "proc4"); if(!st) { - st = rrdset_create("nfs", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Client Remote Procedure Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("nfs", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Client Remote Procedure Calls" + , "calls/s", 5011, update_every, RRDSET_TYPE_STACKED); for(i = 0; nfs_proc4_values[i].present ; i++) - rrddim_add(st, nfs_proc4_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, nfs_proc4_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/proc_net_rpc_nfsd.c b/src/proc_net_rpc_nfsd.c index 02a8c8f90..b0ed58d13 100644 --- a/src/proc_net_rpc_nfsd.c +++ b/src/proc_net_rpc_nfsd.c @@ -210,15 +210,14 @@ struct nfsd_procs nfsd4_ops_values[] = { int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { + (void)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"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_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; @@ -493,13 +492,14 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_rc == 2) { - st = rrdset_find_bytype("nfsd", "readcache"); + st = rrdset_find_bytype_localhost("nfsd", "readcache"); if(!st) { - st = rrdset_create("nfsd", "readcache", NULL, "cache", NULL, "NFS Server Read Cache", "reads/s", 5000, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("nfsd", "readcache", NULL, "cache", NULL, "NFS Server 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); + rrddim_add(st, "hits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "misses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "nocache", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -512,16 +512,17 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_fh == 2) { - st = rrdset_find_bytype("nfsd", "filehandles"); + st = rrdset_find_bytype_localhost("nfsd", "filehandles"); if(!st) { - st = rrdset_create("nfsd", "filehandles", NULL, "filehandles", NULL, "NFS Server 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); + st = rrdset_create_localhost("nfsd", "filehandles", NULL, "filehandles", NULL, "NFS Server File Handles" + , "handles/s", 5001, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "stale", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "total_lookups", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "anonymous_lookups", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "dir_not_in_dcache", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "non_dir_not_in_dcache", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -536,12 +537,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_io == 2) { - st = rrdset_find_bytype("nfsd", "io"); + st = rrdset_find_bytype_localhost("nfsd", "io"); if(!st) { - st = rrdset_create("nfsd", "io", NULL, "io", NULL, "NFS Server I/O", "kilobytes/s", 5002, update_every, RRDSET_TYPE_AREA); + st = rrdset_create_localhost("nfsd", "io", NULL, "io", NULL, "NFS Server 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); + rrddim_add(st, "read", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "write", NULL, -1, 1000, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -553,42 +555,47 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_th == 2) { - st = rrdset_find_bytype("nfsd", "threads"); + st = rrdset_find_bytype_localhost("nfsd", "threads"); if(!st) { - st = rrdset_create("nfsd", "threads", NULL, "threads", NULL, "NFS Server Threads", "threads", 5003, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("nfsd", "threads", NULL, "threads", NULL, "NFS Server Threads", "threads", 5003 + , update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "threads", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "threads", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); rrddim_set(st, "threads", th_threads); rrdset_done(st); - st = rrdset_find_bytype("nfsd", "threads_fullcnt"); + st = rrdset_find_bytype_localhost("nfsd", "threads_fullcnt"); if(!st) { - st = rrdset_create("nfsd", "threads_fullcnt", NULL, "threads", NULL, "NFS Server Threads Full Count", "ops/s", 5004, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("nfsd", "threads_fullcnt", NULL, "threads", NULL + , "NFS Server Threads Full Count", "ops/s", 5004, update_every + , RRDSET_TYPE_LINE); - rrddim_add(st, "full_count", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "full_count", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); rrddim_set(st, "full_count", th_fullcnt); rrdset_done(st); - st = rrdset_find_bytype("nfsd", "threads_histogram"); + st = rrdset_find_bytype_localhost("nfsd", "threads_histogram"); if(!st) { - st = rrdset_create("nfsd", "threads_histogram", NULL, "threads", NULL, "NFS Server 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); + st = rrdset_create_localhost("nfsd", "threads_histogram", NULL, "threads", NULL + , "NFS Server Threads Usage Histogram", "percentage", 5005, update_every + , RRDSET_TYPE_LINE); + + rrddim_add(st, "0%-10%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "10%-20%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "20%-30%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "30%-40%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "40%-50%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "50%-60%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "60%-70%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "70%-80%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "80%-90%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "90%-100%", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -608,21 +615,22 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_ra == 2) { - st = rrdset_find_bytype("nfsd", "readahead"); + st = rrdset_find_bytype_localhost("nfsd", "readahead"); if(!st) { - st = rrdset_create("nfsd", "readahead", NULL, "readahead", NULL, "NFS Server 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); + st = rrdset_create_localhost("nfsd", "readahead", NULL, "readahead", NULL, "NFS Server Read Ahead Depth" + , "percentage", 5005, update_every, RRDSET_TYPE_STACKED); + + rrddim_add(st, "10%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "20%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "30%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "40%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "50%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "60%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "70%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "80%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "90%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "100%", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); } else rrdset_next(st); @@ -646,13 +654,14 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_net == 2) { - st = rrdset_find_bytype("nfsd", "net"); + st = rrdset_find_bytype_localhost("nfsd", "net"); if(!st) { - st = rrdset_create("nfsd", "net", NULL, "network", NULL, "NFS Server Network Statistics", "packets/s", 5007, update_every, RRDSET_TYPE_STACKED); - st->isdetail = 1; + st = rrdset_create_localhost("nfsd", "net", NULL, "network", NULL, "NFS Server Network Statistics" + , "packets/s", 5007, update_every, RRDSET_TYPE_STACKED); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "tcp", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -668,14 +677,16 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_rpc == 2) { - st = rrdset_find_bytype("nfsd", "rpc"); + st = rrdset_find_bytype_localhost("nfsd", "rpc"); if(!st) { - st = rrdset_create("nfsd", "rpc", NULL, "rpc", NULL, "NFS Server Remote Procedure Calls Statistics", "calls/s", 5008, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; - - rrddim_add(st, "calls", 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); + st = rrdset_create_localhost("nfsd", "rpc", NULL, "rpc", NULL + , "NFS Server Remote Procedure Calls Statistics", "calls/s", 5008, update_every + , RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "calls", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "bad_format", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "bad_auth", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -692,12 +703,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { if(do_proc2 == 2) { unsigned int i; - st = rrdset_find_bytype("nfsd", "proc2"); + st = rrdset_find_bytype_localhost("nfsd", "proc2"); if(!st) { - st = rrdset_create("nfsd", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Server Remote Procedure Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("nfsd", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Server Remote Procedure Calls" + , "calls/s", 5009, update_every, RRDSET_TYPE_STACKED); for(i = 0; nfsd_proc2_values[i].present ; i++) - rrddim_add(st, nfsd_proc2_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, nfsd_proc2_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -711,12 +723,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { if(do_proc3 == 2) { unsigned int i; - st = rrdset_find_bytype("nfsd", "proc3"); + st = rrdset_find_bytype_localhost("nfsd", "proc3"); if(!st) { - st = rrdset_create("nfsd", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Server Remote Procedure Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("nfsd", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Server Remote Procedure Calls" + , "calls/s", 5010, update_every, RRDSET_TYPE_STACKED); for(i = 0; nfsd_proc3_values[i].present ; i++) - rrddim_add(st, nfsd_proc3_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, nfsd_proc3_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -730,12 +743,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { if(do_proc4 == 2) { unsigned int i; - st = rrdset_find_bytype("nfsd", "proc4"); + st = rrdset_find_bytype_localhost("nfsd", "proc4"); if(!st) { - st = rrdset_create("nfsd", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Server Remote Procedure Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("nfsd", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Server Remote Procedure Calls" + , "calls/s", 5011, update_every, RRDSET_TYPE_STACKED); for(i = 0; nfsd_proc4_values[i].present ; i++) - rrddim_add(st, nfsd_proc4_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, nfsd_proc4_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -749,12 +763,13 @@ int do_proc_net_rpc_nfsd(int update_every, usec_t dt) { if(do_proc4ops == 2) { unsigned int i; - st = rrdset_find_bytype("nfsd", "proc4ops"); + st = rrdset_find_bytype_localhost("nfsd", "proc4ops"); if(!st) { - st = rrdset_create("nfsd", "proc4ops", NULL, "nfsv2ops", NULL, "NFS v4 Server Operations", "operations/s", 5012, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("nfsd", "proc4ops", NULL, "nfsv2ops", NULL, "NFS v4 Server 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); + rrddim_add(st, nfsd4_ops_values[i].name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/proc_net_snmp.c b/src/proc_net_snmp.c index cd5c250ae..ba7b40013 100644 --- a/src/proc_net_snmp.c +++ b/src/proc_net_snmp.c @@ -355,7 +355,7 @@ int do_proc_net_snmp(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/snmp"); ff = procfile_open(config_get("plugin:proc:/proc/net/snmp", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; } @@ -392,14 +392,15 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_ip_packets) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".packets"); + st = rrdset_find_localhost(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); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "packets", NULL, "packets", NULL, "IPv4 Packets" + , "packets/s", 3000, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InReceives", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutRequests", "sent", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InDelivers", "delivered", 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InReceives", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutRequests", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InDelivers", "delivered", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -413,14 +414,16 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_ip_fragsout) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsout"); + st = rrdset_find_localhost(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, "FragOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "FragFails", "failed", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "FragCreates", "created", 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "fragsout", NULL, "fragments", NULL + , "IPv4 Fragments Sent", "packets/s", 3010, update_every + , RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "FragOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "FragFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "FragCreates", "created", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -433,14 +436,16 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_ip_fragsin) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsin"); + st = rrdset_find_localhost(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, "ReasmOKs", "ok", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "ReasmFails", "failed", -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "ReasmReqds", "all", 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "fragsin", NULL, "fragments", NULL + , "IPv4 Fragments Reassembly", "packets/s", 3011, update_every + , RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "ReasmFails", "failed", -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "ReasmReqds", "all", 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -453,19 +458,20 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_ip_errors) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".errors"); + st = rrdset_find_localhost(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; + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "errors", NULL, "errors", NULL, "IPv4 Errors" + , "packets/s", 3002, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -497,12 +503,13 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_icmp_packets) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".icmp"); + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".icmp"); if(!st) { - st = rrdset_create(RRD_TYPE_NET_SNMP, "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s", 2602, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets" + , "packets/s", 2602, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InMsgs", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutMsgs", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InMsgs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutMsgs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -511,13 +518,15 @@ int do_proc_net_snmp(int update_every, usec_t dt) { rrdset_done(st); - st = rrdset_find(RRD_TYPE_NET_SNMP ".icmp_errors"); + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".icmp_errors"); if(!st) { - st = rrdset_create(RRD_TYPE_NET_SNMP, "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors", "packets/s", 2603, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "icmp_errors", NULL, "icmp", NULL + , "IPv4 ICMP Errors", "packets/s", 2603, 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, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -543,12 +552,13 @@ int do_proc_net_snmp(int update_every, usec_t dt) { if(do_icmpmsg) { int i; - st = rrdset_find(RRD_TYPE_NET_SNMP ".icmpmsg"); + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".icmpmsg"); if(!st) { - st = rrdset_create(RRD_TYPE_NET_SNMP, "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages", "packets/s", 2604, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages" + , "packets/s", 2604, update_every, RRDSET_TYPE_LINE); for(i = 0; icmpmsg_data[i].name ;i++) - rrddim_add(st, icmpmsg_data[i].name, icmpmsg_data[i].label, icmpmsg_data[i].multiplier, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, icmpmsg_data[i].name, icmpmsg_data[i].label, icmpmsg_data[i].multiplier, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -578,11 +588,12 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html if(do_tcp_sockets) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".tcpsock"); + st = rrdset_find_localhost(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); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections" + , "active connections", 2500, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "CurrEstab", "connections", 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "CurrEstab", "connections", 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -593,12 +604,13 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_tcp_packets) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".tcppackets"); + st = rrdset_find_localhost(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); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets" + , "packets/s", 2600, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InSegs", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutSegs", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -610,14 +622,15 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_tcp_errors) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".tcperrors"); + st = rrdset_find_localhost(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; + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors" + , "packets/s", 2700, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InErrs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "RetransSegs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -630,16 +643,18 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_tcp_handshake) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".tcphandshake"); + st = rrdset_find_localhost(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); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", NULL + , "IPv4 TCP Handshake Issues", "events/s", 2900, update_every + , RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "EstabResets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutRsts", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "AttemptFails", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -671,12 +686,13 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // see http://net-snmp.sourceforge.net/docs/mibs/udp.html if(do_udp_packets) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".udppackets"); + st = rrdset_find_localhost(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); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets" + , "packets/s", 2601, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -688,17 +704,18 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_udp_errors) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".udperrors"); + st = rrdset_find_localhost(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); - rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors" + , "events/s", 2701, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -730,12 +747,14 @@ int do_proc_net_snmp(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_udplite_packets) { - st = rrdset_find(RRD_TYPE_NET_SNMP ".udplite"); + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".udplite"); if(!st) { - st = rrdset_create(RRD_TYPE_NET_SNMP, "udplite", NULL, "udplite", NULL, "IPv4 UDPLite Packets", "packets/s", 2603, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "udplite", NULL, "udplite", NULL + , "IPv4 UDPLite Packets", "packets/s", 2603, update_every + , RRDSET_TYPE_LINE); - rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDatagrams", "received", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -743,16 +762,18 @@ int do_proc_net_snmp(int update_every, usec_t dt) { rrddim_set(st, "OutDatagrams", *udplite_OutDatagrams); rrdset_done(st); - st = rrdset_find(RRD_TYPE_NET_SNMP ".udplite_errors"); + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".udplite_errors"); if(!st) { - st = rrdset_create(RRD_TYPE_NET_SNMP, "udplite_errors", NULL, "udplite", NULL, "IPv4 UDPLite Errors", "packets/s", 2604, update_every, RRDSET_TYPE_LINE); - - rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "udplite_errors", NULL, "udplite", NULL + , "IPv4 UDPLite Errors", "packets/s", 2604, update_every + , RRDSET_TYPE_LINE); + + rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/proc_net_snmp6.c b/src/proc_net_snmp6.c index 51d7121a1..8c4581c1b 100644 --- a/src/proc_net_snmp6.c +++ b/src/proc_net_snmp6.c @@ -126,28 +126,28 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { static unsigned long long UdpLite6InCsumErrors = 0ULL; if(unlikely(!arl_base)) { - do_ip_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND); - do_ip_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND); - do_ip_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND); - do_ip_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND); - do_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_ONDEMAND_ONDEMAND); - do_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_ONDEMAND_ONDEMAND); - do_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_ONDEMAND_ONDEMAND); - do_udplite_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_ONDEMAND_ONDEMAND); - do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_ONDEMAND_ONDEMAND); - do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND); - do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND); - do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_ONDEMAND_ONDEMAND); - do_icmp = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_ONDEMAND_ONDEMAND); - do_icmp_redir = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND); - do_icmp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_ONDEMAND_ONDEMAND); - do_icmp_echos = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_ONDEMAND_ONDEMAND); - do_icmp_groupmemb = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_ONDEMAND_ONDEMAND); - do_icmp_router = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_ONDEMAND_ONDEMAND); - do_icmp_neighbor = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND); - do_icmp_mldv2 = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_ONDEMAND_ONDEMAND); - do_icmp_types = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_ONDEMAND_ONDEMAND); - do_ect = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_ONDEMAND_ONDEMAND); + do_ip_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_BOOLEAN_AUTO); + do_ip_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_BOOLEAN_AUTO); + do_ip_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_BOOLEAN_AUTO); + do_ip_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_BOOLEAN_AUTO); + do_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_BOOLEAN_AUTO); + do_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_BOOLEAN_AUTO); + do_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_BOOLEAN_AUTO); + do_udplite_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_BOOLEAN_AUTO); + do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_BOOLEAN_AUTO); + do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_BOOLEAN_AUTO); + do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_BOOLEAN_AUTO); + do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_BOOLEAN_AUTO); + do_icmp = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_BOOLEAN_AUTO); + do_icmp_redir = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_BOOLEAN_AUTO); + do_icmp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_BOOLEAN_AUTO); + do_icmp_echos = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_BOOLEAN_AUTO); + do_icmp_groupmemb = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_BOOLEAN_AUTO); + do_icmp_router = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_BOOLEAN_AUTO); + do_icmp_neighbor = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_BOOLEAN_AUTO); + do_icmp_mldv2 = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_BOOLEAN_AUTO); + do_icmp_types = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_BOOLEAN_AUTO); + do_ect = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_BOOLEAN_AUTO); arl_base = arl_create("snmp6", NULL, 60); arl_expect(arl_base, "Ip6InReceives", &Ip6InReceives); @@ -246,7 +246,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp6"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/snmp6"); ff = procfile_open(config_get("plugin:proc:/proc/net/snmp6", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; @@ -276,14 +276,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (Ip6InOctets || Ip6OutOctets))) { - do_bandwidth = CONFIG_ONDEMAND_YES; - st = rrdset_find("system.ipv6"); + if(do_bandwidth == CONFIG_BOOLEAN_YES || (do_bandwidth == CONFIG_BOOLEAN_AUTO && (Ip6InOctets || Ip6OutOctets))) { + do_bandwidth = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost("system.ipv6"); if(unlikely(!st)) { - st = rrdset_create("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA); + st = rrdset_create_localhost("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); + rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -294,16 +295,17 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_ip_packets == CONFIG_BOOLEAN_YES || (do_ip_packets == CONFIG_BOOLEAN_AUTO && (Ip6InReceives || Ip6OutRequests || Ip6InDelivers || Ip6OutForwDatagrams))) { + do_ip_packets = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".packets"); if(unlikely(!st)) { - st = rrdset_create(RRD_TYPE_NET_SNMP6, "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "forwarded", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "delivers", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -316,16 +318,17 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_ip_fragsout == CONFIG_BOOLEAN_YES || (do_ip_fragsout == CONFIG_BOOLEAN_AUTO && (Ip6FragOKs || Ip6FragFails || Ip6FragCreates))) { + do_ip_fragsout = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".fragsout"); if(unlikely(!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; + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent" + , "packets/s", 3010, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - 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); + rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -337,23 +340,25 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_ip_fragsin == CONFIG_ONDEMAND_YES || (do_ip_fragsin == CONFIG_ONDEMAND_ONDEMAND + if(do_ip_fragsin == CONFIG_BOOLEAN_YES || (do_ip_fragsin == CONFIG_BOOLEAN_AUTO && ( Ip6ReasmOKs || Ip6ReasmFails || Ip6ReasmTimeout || Ip6ReasmReqds ))) { - do_ip_fragsin = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsin"); + do_ip_fragsin = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".fragsin"); if(unlikely(!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); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "fragsin", NULL, "fragments", NULL + , "IPv6 Fragments Reassembly", "packets/s", 3011, update_every + , RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "ok", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "timeout", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "all", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -366,7 +371,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_ip_errors == CONFIG_ONDEMAND_YES || (do_ip_errors == CONFIG_ONDEMAND_ONDEMAND + if(do_ip_errors == CONFIG_BOOLEAN_YES || (do_ip_errors == CONFIG_BOOLEAN_AUTO && ( Ip6InDiscards || Ip6OutDiscards @@ -377,23 +382,24 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { || Ip6InTruncatedPkts || Ip6InNoRoutes ))) { - do_ip_errors = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".errors"); + do_ip_errors = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".errors"); if(unlikely(!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; + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s" + , 3002, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "InDiscards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutDiscards", NULL, -1, 1, RRD_ALGORITHM_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, "InHdrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InTooBigErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -413,14 +419,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_udp_packets == CONFIG_BOOLEAN_YES || (do_udp_packets == CONFIG_BOOLEAN_AUTO && (Udp6InDatagrams || Udp6OutDatagrams))) { + do_udp_packets = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".udppackets"); if(unlikely(!st)) { - st = rrdset_create(RRD_TYPE_NET_SNMP6, "udppackets", NULL, "udp", NULL, "IPv6 UDP Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -431,7 +438,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_udp_errors == CONFIG_ONDEMAND_YES || (do_udp_errors == CONFIG_ONDEMAND_ONDEMAND + if(do_udp_errors == CONFIG_BOOLEAN_YES || (do_udp_errors == CONFIG_BOOLEAN_AUTO && ( Udp6InErrors || Udp6NoPorts @@ -440,18 +447,19 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { || Udp6InCsumErrors || Udp6IgnoredMulti ))) { - do_udp_errors = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udperrors"); + do_udp_errors = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".udperrors"); if(unlikely(!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); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "udperrors", NULL, "udp", NULL, "IPv6 UDP Errors" + , "events/s", 3701, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -466,14 +474,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_udplite_packets == CONFIG_BOOLEAN_YES || (do_udplite_packets == CONFIG_BOOLEAN_AUTO && (UdpLite6InDatagrams || UdpLite6OutDatagrams))) { + do_udplite_packets = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".udplitepackets"); if(unlikely(!st)) { - st = rrdset_create(RRD_TYPE_NET_SNMP6, "udplitepackets", NULL, "udplite", NULL, "IPv6 UDPlite Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -484,7 +493,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_udplite_errors == CONFIG_ONDEMAND_YES || (do_udplite_errors == CONFIG_ONDEMAND_ONDEMAND + if(do_udplite_errors == CONFIG_BOOLEAN_YES || (do_udplite_errors == CONFIG_BOOLEAN_AUTO && ( UdpLite6InErrors || UdpLite6NoPorts @@ -493,17 +502,18 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { || Udp6InCsumErrors || UdpLite6InCsumErrors ))) { - do_udplite_errors = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udpliteerrors"); + do_udplite_errors = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".udpliteerrors"); if(unlikely(!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); + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "udpliteerrors", NULL, "udplite", NULL + , "IPv6 UDP Lite Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "NoPorts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -517,15 +527,17 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_mcast == CONFIG_BOOLEAN_YES || (do_mcast == CONFIG_BOOLEAN_AUTO && (Ip6OutMcastOctets || Ip6InMcastOctets))) { + do_mcast = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".mcast"); if(unlikely(!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; + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "mcast", NULL, "multicast", NULL + , "IPv6 Multicast Bandwidth", "kilobits/s", 9000, update_every + , RRDSET_TYPE_AREA); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -536,15 +548,17 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_bcast == CONFIG_BOOLEAN_YES || (do_bcast == CONFIG_BOOLEAN_AUTO && (Ip6OutBcastOctets || Ip6InBcastOctets))) { + do_bcast = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".bcast"); if(unlikely(!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; + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "bcast", NULL, "broadcast", NULL + , "IPv6 Broadcast Bandwidth", "kilobits/s", 8000, update_every + , RRDSET_TYPE_AREA); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); + rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -555,15 +569,16 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_mcast_p == CONFIG_BOOLEAN_YES || (do_mcast_p == CONFIG_BOOLEAN_AUTO && (Ip6OutMcastPkts || Ip6InMcastPkts))) { + do_mcast_p = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".mcastpkts"); if(unlikely(!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; + st = rrdset_create_localhost(RRD_TYPE_NET_SNMP6, "mcastpkts", NULL, "multicast", NULL + , "IPv6 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -574,14 +589,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_icmp == CONFIG_BOOLEAN_YES || (do_icmp == CONFIG_BOOLEAN_AUTO && (Icmp6InMsgs || Icmp6OutMsgs))) { + do_icmp = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmp"); if(unlikely(!st)) { - st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", "messages/s", 10000, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -592,14 +608,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_icmp_redir == CONFIG_BOOLEAN_YES || (do_icmp_redir == CONFIG_BOOLEAN_AUTO && (Icmp6InRedirects || Icmp6OutRedirects))) { + do_icmp_redir = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmpredir"); if(unlikely(!st)) { - st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", "redirects/s", 10050, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -610,7 +627,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_icmp_errors == CONFIG_ONDEMAND_YES || (do_icmp_errors == CONFIG_ONDEMAND_ONDEMAND + if(do_icmp_errors == CONFIG_BOOLEAN_YES || (do_icmp_errors == CONFIG_BOOLEAN_AUTO && ( Icmp6InErrors || Icmp6OutErrors @@ -624,23 +641,24 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { || Icmp6OutTimeExcds || Icmp6OutParmProblems ))) { - do_icmp_errors = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmperrors"); + do_icmp_errors = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmperrors"); if(unlikely(!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); + st = rrdset_create_localhost(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, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutErrors", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InParmProblems", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutPktTooBigs", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -660,22 +678,23 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_icmp_echos == CONFIG_ONDEMAND_YES || (do_icmp_echos == CONFIG_ONDEMAND_ONDEMAND + if(do_icmp_echos == CONFIG_BOOLEAN_YES || (do_icmp_echos == CONFIG_BOOLEAN_AUTO && ( Icmp6InEchos || Icmp6OutEchos || Icmp6InEchoReplies || Icmp6OutEchoReplies ))) { - do_icmp_echos = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpechos"); + do_icmp_echos = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmpechos"); if(unlikely(!st)) { - st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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); + rrddim_add(st, "InEchos", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutEchos", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -688,7 +707,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_icmp_groupmemb == CONFIG_ONDEMAND_YES || (do_icmp_groupmemb == CONFIG_ONDEMAND_ONDEMAND + if(do_icmp_groupmemb == CONFIG_BOOLEAN_YES || (do_icmp_groupmemb == CONFIG_BOOLEAN_AUTO && ( Icmp6InGroupMembQueries || Icmp6OutGroupMembQueries @@ -697,17 +716,19 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { || Icmp6InGroupMembReductions || Icmp6OutGroupMembReductions ))) { - do_icmp_groupmemb = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".groupmemb"); + do_icmp_groupmemb = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".groupmemb"); if(unlikely(!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); + st = rrdset_create_localhost(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, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutQueries", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InResponses", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutResponses", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InReductions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutReductions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -722,22 +743,23 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_icmp_router == CONFIG_ONDEMAND_YES || (do_icmp_router == CONFIG_ONDEMAND_ONDEMAND + if(do_icmp_router == CONFIG_BOOLEAN_YES || (do_icmp_router == CONFIG_BOOLEAN_AUTO && ( Icmp6InRouterSolicits || Icmp6OutRouterSolicits || Icmp6InRouterAdvertisements || Icmp6OutRouterAdvertisements ))) { - do_icmp_router = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmprouter"); + do_icmp_router = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmprouter"); if(unlikely(!st)) { - st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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); + rrddim_add(st, "InSolicits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -750,22 +772,24 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_icmp_neighbor == CONFIG_ONDEMAND_YES || (do_icmp_neighbor == CONFIG_ONDEMAND_ONDEMAND + if(do_icmp_neighbor == CONFIG_BOOLEAN_YES || (do_icmp_neighbor == CONFIG_BOOLEAN_AUTO && ( Icmp6InNeighborSolicits || Icmp6OutNeighborSolicits || Icmp6InNeighborAdvertisements || Icmp6OutNeighborAdvertisements ))) { - do_icmp_neighbor = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpneighbor"); + do_icmp_neighbor = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmpneighbor"); if(unlikely(!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); + st = rrdset_create_localhost(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, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutSolicits", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -778,14 +802,15 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - 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(do_icmp_mldv2 == CONFIG_BOOLEAN_YES || (do_icmp_mldv2 == CONFIG_BOOLEAN_AUTO && (Icmp6InMLDv2Reports || Icmp6OutMLDv2Reports))) { + do_icmp_mldv2 = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmpmldv2"); if(unlikely(!st)) { - st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpmldv2", NULL, "icmp", NULL, "IPv6 ICMP MLDv2 Reports", "reports/s", 10600, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -796,7 +821,7 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_icmp_types == CONFIG_ONDEMAND_YES || (do_icmp_types == CONFIG_ONDEMAND_ONDEMAND + if(do_icmp_types == CONFIG_BOOLEAN_YES || (do_icmp_types == CONFIG_BOOLEAN_AUTO && ( Icmp6InType1 || Icmp6InType128 @@ -809,21 +834,22 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { || Icmp6OutType135 || Icmp6OutType143 ))) { - do_icmp_types = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmptypes"); + do_icmp_types = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".icmptypes"); if(unlikely(!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); + st = rrdset_create_localhost(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, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InType128", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InType129", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InType136", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType1", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType128", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType129", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType133", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType135", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "OutType143", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -842,22 +868,23 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_ect == CONFIG_ONDEMAND_YES || (do_ect == CONFIG_ONDEMAND_ONDEMAND + if(do_ect == CONFIG_BOOLEAN_YES || (do_ect == CONFIG_BOOLEAN_AUTO && ( Ip6InNoECTPkts || Ip6InECT1Pkts || Ip6InECT0Pkts || Ip6InCEPkts ))) { - do_ect = CONFIG_ONDEMAND_YES; - st = rrdset_find(RRD_TYPE_NET_SNMP6 ".ect"); + do_ect = CONFIG_BOOLEAN_YES; + st = rrdset_find_localhost(RRD_TYPE_NET_SNMP6 ".ect"); if(unlikely(!st)) { - st = rrdset_create(RRD_TYPE_NET_SNMP6, "ect", NULL, "packets", NULL, "IPv6 ECT Packets", "packets/s", 10800, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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); + rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InECT1Pkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InECT0Pkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "InCEPkts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/proc_net_softnet_stat.c b/src/proc_net_softnet_stat.c index 2f4eb3e66..40946a7a5 100644 --- a/src/proc_net_softnet_stat.c +++ b/src/proc_net_softnet_stat.c @@ -24,7 +24,7 @@ int do_proc_net_softnet_stat(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/softnet_stat"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/softnet_stat"); ff = procfile_open(config_get("plugin:proc:/proc/net/softnet_stat", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; } @@ -77,12 +77,13 @@ int do_proc_net_softnet_stat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("system", "softnet_stat"); + st = rrdset_find_bytype_localhost("system", "softnet_stat"); if(unlikely(!st)) { - st = rrdset_create("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat", "events/s", 955, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat" + , "events/s", 955, update_every, RRDSET_TYPE_LINE); for(w = 0; w < allocated_columns ;w++) if(unlikely(softnet_column_name(w))) - rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -97,15 +98,16 @@ int do_proc_net_softnet_stat(int update_every, usec_t dt) { char id[50+1]; snprintfz(id, 50, "cpu%zu_softnet_stat", l); - st = rrdset_find_bytype("cpu", id); + st = rrdset_find_bytype_localhost("cpu", id); if(unlikely(!st)) { char title[100+1]; snprintfz(title, 100, "CPU%zu softnet_stat", l); - st = rrdset_create("cpu", id, NULL, "softnet_stat", NULL, title, "events/s", 4101 + l, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("cpu", id, NULL, "softnet_stat", NULL, title, "events/s", 4101 + l + , update_every, RRDSET_TYPE_LINE); for(w = 0; w < allocated_columns ;w++) if(unlikely(softnet_column_name(w))) - rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/proc_net_stat_conntrack.c b/src/proc_net_stat_conntrack.c index b9c724983..e04b80a3e 100644 --- a/src/proc_net_stat_conntrack.c +++ b/src/proc_net_stat_conntrack.c @@ -16,10 +16,10 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { if(unlikely(do_sockets == -1)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/nf_conntrack"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/stat/nf_conntrack"); nf_conntrack_filename = config_get("plugin:proc:/proc/net/stat/nf_conntrack", "filename to monitor", filename); - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/net/netfilter/nf_conntrack_max"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/sys/net/netfilter/nf_conntrack_max"); nf_conntrack_max_filename = config_get("plugin:proc:/proc/sys/net/netfilter/nf_conntrack_max", "filename to monitor", filename); usec_since_last_max = get_max_every = config_get_number("plugin:proc:/proc/sys/net/netfilter/nf_conntrack_max", "read every seconds", 10) * USEC_PER_SEC; @@ -35,7 +35,7 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { do_sockets = 1; if(!read_full) { - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/net/netfilter/nf_conntrack_count"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/sys/net/netfilter/nf_conntrack_count"); nf_conntrack_count_filename = config_get("plugin:proc:/proc/sys/net/netfilter/nf_conntrack_count", "filename to monitor", filename); if(read_single_number_file(nf_conntrack_count_filename, &aentries)) @@ -47,7 +47,7 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { if(!do_sockets && !read_full) return 1; - rrdvar_max = rrdvar_custom_host_variable_create(&localhost, "netfilter.conntrack.max"); + rrdvar_max = rrdvar_custom_host_variable_create(localhost, "netfilter.conntrack.max"); } if(likely(read_full)) { @@ -130,11 +130,13 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_sockets) { - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets"); if(unlikely(!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", 3000, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL + , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections" + , "active connections", 3000, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "connections", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -145,13 +147,15 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_new) { - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new"); if(unlikely(!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", 3001, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL + , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections" + , "connections/s", 3001, 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); + rrddim_add(st, "new", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "ignore", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "invalid", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -164,14 +168,16 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_changes) { - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes"); if(unlikely(!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", 3002, 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); + st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL + , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s" + , 3002, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "inserted", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "deleted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "delete_list", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -184,14 +190,16 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_expect) { - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect"); if(unlikely(!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", 3003, 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); + st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL + , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations" + , "expectations/s", 3003, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "created", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "deleted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "new", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -204,14 +212,16 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_search) { - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search"); if(unlikely(!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", 3010, 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); + st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL + , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches" + , "searches/s", 3010, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "searched", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "restarted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "found", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -224,15 +234,17 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(do_errors) { - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors"); if(unlikely(!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", 3005, 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); + st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL + , RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s" + , 3005, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + + rrddim_add(st, "icmp_error", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "insert_failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "drop", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "early_drop", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/proc_net_stat_synproxy.c b/src/proc_net_stat_synproxy.c index 6bb0a3c69..5a1fc30eb 100644 --- a/src/proc_net_stat_synproxy.c +++ b/src/proc_net_stat_synproxy.c @@ -10,15 +10,15 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) { static procfile *ff = NULL; if(unlikely(do_entries == -1)) { - do_entries = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_ONDEMAND_ONDEMAND); - do_cookies = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_ONDEMAND_ONDEMAND); - do_syns = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_ONDEMAND_ONDEMAND); - do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_ONDEMAND_ONDEMAND); + do_entries = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_BOOLEAN_AUTO); + do_cookies = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_BOOLEAN_AUTO); + do_syns = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_BOOLEAN_AUTO); + do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_BOOLEAN_AUTO); } if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/synproxy"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_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(unlikely(!ff)) return 1; @@ -57,14 +57,16 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if((do_entries == CONFIG_ONDEMAND_ONDEMAND && events) || do_entries == CONFIG_ONDEMAND_YES) { - do_entries = CONFIG_ONDEMAND_YES; + if((do_entries == CONFIG_BOOLEAN_AUTO && events) || do_entries == CONFIG_BOOLEAN_YES) { + do_entries = CONFIG_BOOLEAN_YES; - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries"); if(unlikely(!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", 3304, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL + , RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 3304 + , update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "entries", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(st, "entries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -74,14 +76,16 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if((do_syns == CONFIG_ONDEMAND_ONDEMAND && events) || do_syns == CONFIG_ONDEMAND_YES) { - do_syns = CONFIG_ONDEMAND_YES; + if((do_syns == CONFIG_BOOLEAN_AUTO && events) || do_syns == CONFIG_BOOLEAN_YES) { + do_syns = CONFIG_BOOLEAN_YES; - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received"); if(unlikely(!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", 3301, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(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" + , 3301, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -91,14 +95,16 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if((do_reopened == CONFIG_ONDEMAND_ONDEMAND && events) || do_reopened == CONFIG_ONDEMAND_YES) { - do_reopened = CONFIG_ONDEMAND_YES; + if((do_reopened == CONFIG_BOOLEAN_AUTO && events) || do_reopened == CONFIG_BOOLEAN_YES) { + do_reopened = CONFIG_BOOLEAN_YES; - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened"); if(unlikely(!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", 3303, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL + , RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened" + , "connections/s", 3303, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "reopened", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "reopened", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -108,16 +114,18 @@ int do_proc_net_stat_synproxy(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if((do_cookies == CONFIG_ONDEMAND_ONDEMAND && events) || do_cookies == CONFIG_ONDEMAND_YES) { - do_cookies = CONFIG_ONDEMAND_YES; + if((do_cookies == CONFIG_BOOLEAN_AUTO && events) || do_cookies == CONFIG_BOOLEAN_YES) { + do_cookies = CONFIG_BOOLEAN_YES; - st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies"); + st = rrdset_find_localhost(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies"); if(unlikely(!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", 3302, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL + , RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 3302 + , 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); + rrddim_add(st, "valid", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "invalid", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st, "retransmits", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); diff --git a/src/proc_self_mountinfo.c b/src/proc_self_mountinfo.c index d07f22510..bb031a9ab 100644 --- a/src/proc_self_mountinfo.c +++ b/src/proc_self_mountinfo.c @@ -175,10 +175,10 @@ static inline int is_read_only(const char *s) { // read the whole mountinfo into a linked list struct mountinfo *mountinfo_read(int do_statvfs) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", global_host_prefix); + snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", netdata_configured_host_prefix); procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) { - snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", global_host_prefix); + snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", netdata_configured_host_prefix); ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return NULL; } @@ -293,7 +293,7 @@ struct mountinfo *mountinfo_read(int do_statvfs) { struct mountinfo *mt; for(mt = root; mt; mt = mt->next) { - if(unlikely(mt->st_dev == mi->st_dev && !(mi->flags & MOUNTINFO_NO_STAT))) { + if(unlikely(mt->st_dev == mi->st_dev && !(mt->flags & MOUNTINFO_IS_SAME_DEV))) { if(strlen(mi->mount_point) < strlen(mt->mount_point)) mt->flags |= MOUNTINFO_IS_SAME_DEV; else @@ -319,7 +319,7 @@ struct mountinfo *mountinfo_read(int do_statvfs) { } // check if it has size - if(do_statvfs) { + if(do_statvfs && !(mi->flags & MOUNTINFO_IS_DUMMY)) { struct statvfs buff_statvfs; if(unlikely(statvfs(mi->mount_point, &buff_statvfs) < 0)) { mi->flags |= MOUNTINFO_NO_STAT; diff --git a/src/proc_softirqs.c b/src/proc_softirqs.c index c7b10d70d..560e2acb2 100644 --- a/src/proc_softirqs.c +++ b/src/proc_softirqs.c @@ -58,7 +58,7 @@ int do_proc_softirqs(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/softirqs"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/softirqs"); ff = procfile_open(config_get("plugin:proc:/proc/softirqs", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; } @@ -128,8 +128,9 @@ int do_proc_softirqs(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find_bytype("system", "softirqs"); - if(unlikely(!st)) st = rrdset_create("system", "softirqs", NULL, "softirqs", NULL, "System softirqs", "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED); + st = rrdset_find_bytype_localhost("system", "softirqs"); + if(unlikely(!st)) st = rrdset_create_localhost("system", "softirqs", NULL, "softirqs", NULL, "System softirqs" + , "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED); else rrdset_next(st); for(l = 0; l < lines ;l++) { @@ -141,7 +142,7 @@ int do_proc_softirqs(int update_every, usec_t dt) { if(unlikely(!irr->rd || strncmp(irr->name, irr->rd->name, MAX_INTERRUPT_NAME) != 0)) { irr->rd = rrddim_find(st, irr->id); if(unlikely(!irr->rd)) - irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); + irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); else rrddim_set_name(st, irr->rd, irr->name); @@ -163,7 +164,7 @@ int do_proc_softirqs(int update_every, usec_t dt) { char id[50+1]; snprintfz(id, 50, "cpu%d_softirqs", c); - st = rrdset_find_bytype("cpu", id); + st = rrdset_find_bytype_localhost("cpu", id); if(unlikely(!st)) { // find if everything is zero unsigned long long core_sum = 0 ; @@ -176,7 +177,8 @@ int do_proc_softirqs(int update_every, usec_t dt) { char title[100+1]; snprintfz(title, 100, "CPU%d softirqs", c); - st = rrdset_create("cpu", id, NULL, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost("cpu", id, NULL, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c + , update_every, RRDSET_TYPE_STACKED); } else rrdset_next(st); @@ -186,7 +188,7 @@ int do_proc_softirqs(int update_every, usec_t dt) { if(unlikely(!irr->cpu[c].rd)) { irr->cpu[c].rd = rrddim_find(st, irr->id); if(unlikely(!irr->cpu[c].rd)) - irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); + irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); else rrddim_set_name(st, irr->cpu[c].rd, irr->name); } diff --git a/src/proc_stat.c b/src/proc_stat.c index f7e6d5bc0..04f0896cd 100644 --- a/src/proc_stat.c +++ b/src/proc_stat.c @@ -24,7 +24,7 @@ int do_proc_stat(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/stat"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_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; } @@ -91,24 +91,25 @@ int do_proc_stat(int update_every, usec_t dt) { } if(likely((isthistotal && do_cpu) || (!isthistotal && do_cpu_cores))) { - st = rrdset_find_bytype(type, id); + st = rrdset_find_bytype_localhost(type, id); if(unlikely(!st)) { - st = rrdset_create(type, id, NULL, family, context, title, "percentage", priority, update_every, RRDSET_TYPE_STACKED); + st = rrdset_create_localhost(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_add(st, "guest_nice", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "guest", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "steal", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "softirq", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "irq", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "user", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "system", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "nice", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rrddim_add(st, "iowait", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + + rrddim_add(st, "idle", NULL, multiplier, divisor, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); rrddim_hide(st, "idle"); } else rrdset_next(st); @@ -132,12 +133,13 @@ int do_proc_stat(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(likely(do_interrupts)) { - st = rrdset_find_bytype("system", "intr"); + st = rrdset_find_bytype_localhost("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; + st = rrdset_create_localhost("system", "intr", NULL, "interrupts", NULL, "CPU Interrupts" + , "interrupts/s", 900, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "interrupts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -151,11 +153,12 @@ int do_proc_stat(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(likely(do_context)) { - st = rrdset_find_bytype("system", "ctxt"); + st = rrdset_find_bytype_localhost("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); + st = rrdset_create_localhost("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); + rrddim_add(st, "switches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -177,12 +180,13 @@ int do_proc_stat(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(likely(do_forks)) { - st = rrdset_find_bytype("system", "forks"); + st = rrdset_find_bytype_localhost("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; + st = rrdset_create_localhost("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s" + , 700, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); - rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st, "started", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); @@ -193,12 +197,13 @@ int do_proc_stat(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(likely(do_processes)) { - st = rrdset_find_bytype("system", "processes"); + st = rrdset_find_bytype_localhost("system", "processes"); if(unlikely(!st)) { - st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE); + st = rrdset_create_localhost("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); + rrddim_add(st, "running", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "blocked", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); diff --git a/src/proc_sys_kernel_random_entropy_avail.c b/src/proc_sys_kernel_random_entropy_avail.c index 388406e0b..fea8900d3 100644 --- a/src/proc_sys_kernel_random_entropy_avail.c +++ b/src/proc_sys_kernel_random_entropy_avail.c @@ -7,7 +7,7 @@ int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/kernel/random/entropy_avail"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_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(unlikely(!ff)) return 1; } @@ -17,10 +17,11 @@ int do_proc_sys_kernel_random_entropy_avail(int update_every, usec_t dt) { unsigned long long entropy = str2ull(procfile_lineword(ff, 0, 0)); - RRDSET *st = rrdset_find_bytype("system", "entropy"); + RRDSET *st = rrdset_find_bytype_localhost("system", "entropy"); if(unlikely(!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); + st = rrdset_create_localhost("system", "entropy", NULL, "entropy", NULL, "Available Entropy", "entropy", 1000 + , update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "entropy", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); diff --git a/src/proc_uptime.c b/src/proc_uptime.c index 9f341a33f..f74cccb97 100644 --- a/src/proc_uptime.c +++ b/src/proc_uptime.c @@ -13,7 +13,7 @@ int do_proc_uptime(int update_every, usec_t dt) { if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/uptime"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/uptime"); ff = procfile_open(config_get("plugin:proc:/proc/uptime", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) @@ -39,11 +39,12 @@ int do_proc_uptime(int update_every, usec_t dt) { // -------------------------------------------------------------------- if(unlikely(!st)) - st = rrdset_find("system.uptime"); + st = rrdset_find_localhost("system.uptime"); if(unlikely(!st)) { - st = rrdset_create("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000, update_every, RRDSET_TYPE_LINE); - rrddim_add(st, "uptime", NULL, 1, 1000, RRDDIM_ABSOLUTE); + st = rrdset_create_localhost("system", "uptime", NULL, "uptime", NULL, "System Uptime", "seconds", 1000 + , update_every, RRDSET_TYPE_LINE); + rrddim_add(st, "uptime", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); diff --git a/src/proc_vmstat.c b/src/proc_vmstat.c index ea917b989..847487363 100644 --- a/src/proc_vmstat.c +++ b/src/proc_vmstat.c @@ -25,10 +25,10 @@ int do_proc_vmstat(int update_every, usec_t dt) { static unsigned long long pswpout = 0ULL; if(unlikely(!arl_base)) { - do_swapio = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "swap i/o", CONFIG_ONDEMAND_ONDEMAND); + do_swapio = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "swap i/o", CONFIG_BOOLEAN_AUTO); do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1); do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1); - do_numa = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "system-wide numa metric summary", CONFIG_ONDEMAND_ONDEMAND); + do_numa = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "system-wide numa metric summary", CONFIG_BOOLEAN_AUTO); arl_base = arl_create("vmstat", NULL, 60); @@ -39,7 +39,7 @@ int do_proc_vmstat(int update_every, usec_t dt) { arl_expect(arl_base, "pswpin", &pswpin); arl_expect(arl_base, "pswpout", &pswpout); - if(do_numa == CONFIG_ONDEMAND_YES || (do_numa == CONFIG_ONDEMAND_ONDEMAND && get_numa_node_count() >= 2)) { + if(do_numa == CONFIG_BOOLEAN_YES || (do_numa == CONFIG_BOOLEAN_AUTO && get_numa_node_count() >= 2)) { arl_expect(arl_base, "numa_foreign", &numa_foreign); arl_expect(arl_base, "numa_hint_faults_local", &numa_hint_faults_local); arl_expect(arl_base, "numa_hint_faults", &numa_hint_faults); @@ -56,13 +56,13 @@ int do_proc_vmstat(int update_every, usec_t dt) { // when all the expected metrics are collected. // Also ARL will not parse their values. has_numa = 0; - do_numa = CONFIG_ONDEMAND_NO; + do_numa = CONFIG_BOOLEAN_NO; } } if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/vmstat"); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/vmstat"); ff = procfile_open(config_get("plugin:proc:/proc/vmstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; } @@ -87,15 +87,16 @@ int do_proc_vmstat(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(pswpin || pswpout || do_swapio == CONFIG_ONDEMAND_YES) { - do_swapio = CONFIG_ONDEMAND_YES; + if(pswpin || pswpout || do_swapio == CONFIG_BOOLEAN_YES) { + do_swapio = CONFIG_BOOLEAN_YES; static RRDSET *st_swapio = NULL; if(unlikely(!st_swapio)) { - st_swapio = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA); + st_swapio = rrdset_create_localhost("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); + rrddim_add(st_swapio, "in", NULL, sysconf(_SC_PAGESIZE), 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_swapio, "out", NULL, -sysconf(_SC_PAGESIZE), 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st_swapio); @@ -109,10 +110,11 @@ int do_proc_vmstat(int update_every, usec_t dt) { if(do_io) { static RRDSET *st_io = NULL; if(unlikely(!st_io)) { - st_io = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA); + st_io = rrdset_create_localhost("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); + rrddim_add(st_io, "in", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_io, "out", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st_io); @@ -126,11 +128,12 @@ int do_proc_vmstat(int update_every, usec_t dt) { if(do_pgfaults) { static RRDSET *st_pgfaults = NULL; if(unlikely(!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; + st_pgfaults = rrdset_create_localhost("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults" + , "page faults/s", 500, update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st_pgfaults, RRDSET_FLAG_DETAIL); - rrddim_add(st_pgfaults, "minor", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st_pgfaults, "major", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_pgfaults, "minor", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_pgfaults, "major", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st_pgfaults); @@ -149,27 +152,28 @@ int do_proc_vmstat(int update_every, usec_t dt) { has_numa = (numa_local || numa_foreign || numa_interleave || numa_other || numa_pte_updates || numa_huge_pte_updates || numa_hint_faults || numa_hint_faults_local || numa_pages_migrated) ? 1 : 0; - if(do_numa == CONFIG_ONDEMAND_YES || (do_numa == CONFIG_ONDEMAND_ONDEMAND && has_numa)) { - do_numa = CONFIG_ONDEMAND_YES; + if(do_numa == CONFIG_BOOLEAN_YES || (do_numa == CONFIG_BOOLEAN_AUTO && has_numa)) { + do_numa = CONFIG_BOOLEAN_YES; static RRDSET *st_numa = NULL; if(unlikely(!st_numa)) { - st_numa = rrdset_create("mem", "numa", NULL, "numa", NULL, "NUMA events", "events/s", 800, update_every, RRDSET_TYPE_LINE); - st_numa->isdetail = 1; + st_numa = rrdset_create_localhost("mem", "numa", NULL, "numa", NULL, "NUMA events", "events/s", 800 + , update_every, RRDSET_TYPE_LINE); + rrdset_flag_set(st_numa, RRDSET_FLAG_DETAIL); // These depend on CONFIG_NUMA in the kernel. - rrddim_add(st_numa, "local", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st_numa, "foreign", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st_numa, "interleave", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st_numa, "other", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_numa, "local", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_numa, "foreign", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_numa, "interleave", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_numa, "other", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); // The following stats depend on CONFIG_NUMA_BALANCING in the // kernel. - rrddim_add(st_numa, "pte updates", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st_numa, "huge pte updates", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st_numa, "hint faults", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st_numa, "hint faults local", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st_numa, "pages migrated", NULL, 1, 1, RRDDIM_INCREMENTAL); + rrddim_add(st_numa, "pte updates", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_numa, "huge pte updates", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_numa, "hint faults", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_numa, "hint faults local", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(st_numa, "pages migrated", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st_numa); diff --git a/src/procfile.c b/src/procfile.c index 6f52bf465..3a89e8353 100644 --- a/src/procfile.c +++ b/src/procfile.c @@ -15,6 +15,27 @@ size_t procfile_max_lines = PFLINES_INCREASE_STEP; size_t procfile_max_words = PFWORDS_INCREASE_STEP; size_t procfile_max_allocation = PROCFILE_INCREMENT_BUFFER; + +// ---------------------------------------------------------------------------- + +char *procfile_filename(procfile *ff) { + if(ff->filename[0]) return ff->filename; + + char buffer[FILENAME_MAX + 1]; + snprintfz(buffer, FILENAME_MAX, "/proc/self/fd/%d", ff->fd); + + ssize_t l = readlink(buffer, ff->filename, FILENAME_MAX); + if(unlikely(l == -1)) + snprintfz(ff->filename, FILENAME_MAX, "unknown filename for fd %d", ff->fd); + else + ff->filename[l] = '\0'; + + // on non-linux systems, something like this will be needed + // fcntl(ff->fd, F_GETPATH, ff->filename) + + return ff->filename; +} + // ---------------------------------------------------------------------------- // An array of words @@ -106,15 +127,10 @@ static inline void pflines_free(pflines *fl) { // ---------------------------------------------------------------------------- // The procfile -#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); + if(unlikely(!ff)) return; + + debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", procfile_filename(ff)); if(likely(ff->lines)) pflines_free(ff->lines); if(likely(ff->words)) pfwords_free(ff->words); @@ -126,113 +142,119 @@ void procfile_close(procfile *ff) { static inline void procfile_parser(procfile *ff) { // debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename); - register char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data; - register char *separators = ff->separators; - char quote = 0; - size_t l = 0, w = 0, opened = 0; + char *s = ff->data // our current position + , *e = &ff->data[ff->len] // the terminating null + , *t = ff->data; // the first character of a quoted or a parenthesized string + + // the look up array to find our type of character + PF_CHAR_TYPE *separators = ff->separators; + + char quote = 0; // the quote character - only when in quoted string + + size_t + l = 0 // counts the number of lines we added + , w = 0 // counts the number of words we added + , opened = 0; // counts the number of open parenthesis ff->lines = pflines_add(ff->lines, w); while(likely(s < e)) { // we are not at the end + PF_CHAR_TYPE ct = separators[(unsigned char)(*s)]; - switch(separators[(unsigned char)(*s)]) { - case PF_CHAR_IS_OPEN: - if(s == t) { - opened++; - t = ++s; - } - else if(opened) { - opened++; - s++; - } - else - s++; - break; - - case PF_CHAR_IS_CLOSE: - if(opened) { - opened--; - - if(!opened) { - *s = '\0'; - ff->words = pfwords_add(ff->words, t); - ff->lines->lines[l].words++; - w++; - - t = ++s; - } - else - s++; - } - else - s++; - break; + // this is faster than a switch() + if(likely(ct == PF_CHAR_IS_WORD)) { + s++; + } + else if(likely(ct == PF_CHAR_IS_SEPARATOR)) { + if(unlikely(quote || opened)) { + // we are inside a quote + 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; + if(unlikely(s == t)) { + // skip all leading white spaces + t = ++s; + continue; + } - *s = '\0'; - ff->words = pfwords_add(ff->words, t); - ff->lines->lines[l].words++; - w++; + // end of word + *s = '\0'; - t = ++s; - } - else - s++; - break; + ff->words = pfwords_add(ff->words, t); + ff->lines->lines[l].words++; + w++; - case PF_CHAR_IS_SEPARATOR: - if(unlikely(quote || opened)) { - // we are inside a quote - s++; - break; - } + t = ++s; + } + else if(likely(ct == PF_CHAR_IS_NEWLINE)) { + // end of line + *s = '\0'; - if(unlikely(s == t)) { - // skip all leading white spaces - t = ++s; - break; - } + ff->words = pfwords_add(ff->words, t); + ff->lines->lines[l].words++; + w++; - // end of word - *s = '\0'; + // debug(D_PROCFILE, PF_PREFIX ": ended line %d with %d words", l, ff->lines->lines[l].words); - ff->words = pfwords_add(ff->words, t); - ff->lines->lines[l].words++; - w++; + ff->lines = pflines_add(ff->lines, w); + l++; + t = ++s; + } + else if(likely(ct == PF_CHAR_IS_QUOTE)) { + if(unlikely(!quote && s == t)) { + // quote opened at the beginning + quote = *s; t = ++s; - break; + } + else if(unlikely(quote && quote == *s)) { + // quote closed + quote = 0; - case PF_CHAR_IS_NEWLINE: - // end of line *s = '\0'; - ff->words = pfwords_add(ff->words, t); 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); - l++; - t = ++s; - break; + } + else + s++; + } + else if(likely(ct == PF_CHAR_IS_OPEN)) { + if(s == t) { + opened++; + t = ++s; + } + else if(opened) { + opened++; + s++; + } + else + s++; + } + else if(likely(ct == PF_CHAR_IS_CLOSE)) { + if(opened) { + opened--; - default: + if(!opened) { + *s = '\0'; + ff->words = pfwords_add(ff->words, t); + ff->lines->lines[l].words++; + w++; + + t = ++s; + } + else + s++; + } + else s++; - break; } + else + fatal("Internal Error: procfile_readall() does not handle all the cases."); } if(likely(s > t && t < e)) { @@ -250,26 +272,24 @@ static inline void procfile_parser(procfile *ff) { } procfile *procfile_readall(procfile *ff) { - debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename); + // debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename); - ssize_t r = 1; - ff->len = 0; - - while(likely(r > 0)) { + ff->len = 0; // zero the used size + ssize_t r = 1; // read at least once + while(r > 0) { ssize_t s = ff->len; ssize_t x = ff->size - s; if(unlikely(!x)) { - debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename); - + debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", procfile_filename(ff)); 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); + debug(D_PROCFILE, "Reading file '%s', from position %zd with length %zd", procfile_filename(ff), s, (ssize_t)(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); + if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", procfile_filename(ff)); procfile_close(ff); return NULL; } @@ -277,9 +297,9 @@ procfile *procfile_readall(procfile *ff) { ff->len += r; } - debug(D_PROCFILE, "Rewinding file '%s'", ff->filename); + // 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); + if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", procfile_filename(ff)); procfile_close(ff); return NULL; } @@ -294,29 +314,37 @@ procfile *procfile_readall(procfile *ff) { if(unlikely(ff->words->len > procfile_max_words)) procfile_max_words = ff->words->len; } - debug(D_PROCFILE, "File '%s' updated.", ff->filename); + // 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 }; +static inline void procfile_set_separators(procfile *ff, const char *separators) { + static PF_CHAR_TYPE def[256]; + static char initilized = 0; - if(unlikely(!def[255])) { + if(unlikely(!initilized)) { // 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 - int i; - 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; + // if initialized is zero, multiple threads may be executing + // this code at the same time, setting in def[] the exact same values + int i = 256; + while(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; } + + initilized = 1; } // copy the default - char *ffs = ff->separators, *ffd = def, *ffe = &def[256]; - while(likely(ffd != ffe)) *ffs++ = *ffd++; + PF_CHAR_TYPE *ffs = ff->separators, *ffd = def, *ffe = &def[256]; + while(ffd != ffe) + *ffs++ = *ffd++; // set the separators if(unlikely(!separators)) @@ -324,47 +352,50 @@ static void procfile_set_separators(procfile *ff, const char *separators) { ffs = ff->separators; const char *s = separators; - while(likely(*s)) + while(*s) ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR; } void procfile_set_quotes(procfile *ff, const char *quotes) { + PF_CHAR_TYPE *ffs = ff->separators; + // remove all quotes - int i; - for(i = 0; i < 256 ; i++) - if(unlikely(ff->separators[i] == PF_CHAR_IS_QUOTE)) - ff->separators[i] = PF_CHAR_IS_WORD; + int i = 256; + while(i--) + if(unlikely(ffs[i] == PF_CHAR_IS_QUOTE)) + ffs[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)) + while(*s) ffs[(int)*s++] = PF_CHAR_IS_QUOTE; } void procfile_set_open_close(procfile *ff, const char *open, const char *close) { + PF_CHAR_TYPE *ffs = ff->separators; + // remove all open/close - int i; - for(i = 0; i < 256 ; i++) - if(unlikely(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE)) - ff->separators[i] = PF_CHAR_IS_WORD; + int i = 256; + while(i--) + if(unlikely(ffs[i] == PF_CHAR_IS_OPEN || ffs[i] == PF_CHAR_IS_CLOSE)) + ffs[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)) + while(*s) ffs[(int)*s++] = PF_CHAR_IS_OPEN; + // set the closings s = close; - while(likely(*s)) + while(*s) ffs[(int)*s++] = PF_CHAR_IS_CLOSE; } @@ -379,7 +410,9 @@ procfile *procfile_open(const char *filename, const char *separators, uint32_t f 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); + + //strncpyz(ff->filename, filename, FILENAME_MAX); + ff->filename[0] = '\0'; ff->fd = fd; ff->size = size; @@ -406,7 +439,8 @@ procfile *procfile_reopen(procfile *ff, const char *filename, const char *separa return NULL; } - strncpyz(ff->filename, filename, FILENAME_MAX); + //strncpyz(ff->filename, filename, FILENAME_MAX); + ff->filename[0] = '\0'; ff->flags = flags; @@ -423,7 +457,7 @@ void procfile_print(procfile *ff) { size_t lines = procfile_lines(ff), l; char *s; - debug(D_PROCFILE, "File '%s' with %zu lines and %zu words", ff->filename, ff->lines->len, ff->words->len); + debug(D_PROCFILE, "File '%s' with %zu lines and %zu words", procfile_filename(ff), ff->lines->len, ff->words->len); for(l = 0; likely(l < lines) ;l++) { size_t words = procfile_linewords(ff, l); diff --git a/src/procfile.h b/src/procfile.h index a586ba48d..98765697f 100644 --- a/src/procfile.h +++ b/src/procfile.h @@ -58,15 +58,25 @@ typedef struct { #define PROCFILE_FLAG_DEFAULT 0x00000000 #define PROCFILE_FLAG_NO_ERROR_ON_FILE_IO 0x00000001 +typedef enum procfile_separator { + PF_CHAR_IS_SEPARATOR, + PF_CHAR_IS_NEWLINE, + PF_CHAR_IS_WORD, + PF_CHAR_IS_QUOTE, + PF_CHAR_IS_OPEN, + PF_CHAR_IS_CLOSE +} PF_CHAR_TYPE; + typedef struct { - char filename[FILENAME_MAX + 1]; + char filename[FILENAME_MAX + 1]; // not populated until profile_filename() is called + 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]; + PF_CHAR_TYPE separators[256]; char data[]; // allocated buffer to keep file contents } procfile; @@ -89,6 +99,8 @@ extern void procfile_print(procfile *ff); extern void procfile_set_quotes(procfile *ff, const char *quotes); extern void procfile_set_open_close(procfile *ff, const char *open, const char *close); +extern char *procfile_filename(procfile *ff); + // ---------------------------------------------------------------------------- // set this to 1, to have procfile adapt its initial buffer allocation to the max allocation used so far diff --git a/src/registry.c b/src/registry.c index d223cd6f1..ed9be9848 100644 --- a/src/registry.c +++ b/src/registry.c @@ -10,11 +10,11 @@ // REGISTRY concurrency locking static inline void registry_lock(void) { - pthread_mutex_lock(®istry.lock); + netdata_mutex_lock(®istry.lock); } static inline void registry_unlock(void) { - pthread_mutex_unlock(®istry.lock); + netdata_mutex_unlock(®istry.lock); } @@ -41,19 +41,19 @@ static inline void registry_set_person_cookie(struct web_client *w, REGISTRY_PER // ---------------------------------------------------------------------------- // JSON GENERATION -static inline void registry_json_header(struct web_client *w, const char *action, const char *status) { +static inline void registry_json_header(RRDHOST *host, struct web_client *w, const char *action, const char *status) { 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); + action, status, (host == localhost)?registry.hostname:host->hostname, host->machine_guid); } static inline void registry_json_footer(struct web_client *w) { buffer_strcat(w->response.data, "\n}\n"); } -static inline int registry_json_disabled(struct web_client *w, const char *action) { - registry_json_header(w, action, REGISTRY_STATUS_DISABLED); +static inline int registry_json_disabled(RRDHOST *host, struct web_client *w, const char *action) { + registry_json_header(host, w, action, REGISTRY_STATUS_DISABLED); buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"", registry.registry_to_announce); @@ -127,8 +127,8 @@ static inline int registry_person_url_callback_verify_machine_exists(void *entry // ---------------------------------------------------------------------------- // public HELLO request -int registry_request_hello_json(struct web_client *w) { - registry_json_header(w, "hello", REGISTRY_STATUS_OK); +int registry_request_hello_json(RRDHOST *host, struct web_client *w) { + registry_json_header(host, w, "hello", REGISTRY_STATUS_OK); buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"", registry.registry_to_announce); @@ -143,9 +143,9 @@ int registry_request_hello_json(struct web_client *w) { #define REGISTRY_VERIFY_COOKIES_GUID "give-me-back-this-cookie-now--please" // 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) { +int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) { if(unlikely(!registry.enabled)) - return registry_json_disabled(w, "access"); + return registry_json_disabled(host, w, "access"); // ------------------------------------------------------------------------ // verify the browser supports cookies @@ -167,7 +167,7 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char * REGISTRY_PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when); if(!p) { - registry_json_header(w, "access", REGISTRY_STATUS_FAILED); + registry_json_header(host, w, "access", REGISTRY_STATUS_FAILED); registry_json_footer(w); registry_unlock(); return 412; @@ -177,7 +177,7 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char * registry_set_person_cookie(w, p); // generate the response - registry_json_header(w, "access", REGISTRY_STATUS_OK); + registry_json_header(host, 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 }; @@ -193,22 +193,22 @@ int registry_request_access_json(struct web_client *w, char *person_guid, char * // public DELETE request // 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) { +int registry_request_delete_json(RRDHOST *host, 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"); + return registry_json_disabled(host, w, "delete"); registry_lock(); REGISTRY_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_header(host, w, "delete", REGISTRY_STATUS_FAILED); registry_json_footer(w); registry_unlock(); return 412; } // generate the response - registry_json_header(w, "delete", REGISTRY_STATUS_OK); + registry_json_header(host, w, "delete", REGISTRY_STATUS_OK); registry_json_footer(w); registry_unlock(); return 200; @@ -218,21 +218,21 @@ int registry_request_delete_json(struct web_client *w, char *person_guid, char * // public SEARCH request // 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) { +int registry_request_search_json(RRDHOST *host, 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"); + return registry_json_disabled(host, w, "search"); registry_lock(); REGISTRY_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_header(host, w, "search", REGISTRY_STATUS_FAILED); registry_json_footer(w); registry_unlock(); return 404; } - registry_json_header(w, "search", REGISTRY_STATUS_OK); + registry_json_header(host, 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 }; @@ -248,9 +248,9 @@ int registry_request_search_json(struct web_client *w, char *person_guid, char * // SWITCH REQUEST // 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) { +int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when) { if(!registry.enabled) - return registry_json_disabled(w, "switch"); + return registry_json_disabled(host, w, "switch"); (void)url; (void)when; @@ -259,7 +259,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char * REGISTRY_PERSON *op = registry_person_find(person_guid); if(!op) { - registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); + registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); registry_unlock(); return 430; @@ -267,7 +267,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char * REGISTRY_PERSON *np = registry_person_find(new_person_guid); if(!np) { - registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); + registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); registry_unlock(); return 431; @@ -275,7 +275,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char * REGISTRY_MACHINE *m = registry_machine_find(machine_guid); if(!m) { - registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); + registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); registry_unlock(); return 432; @@ -286,7 +286,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char * // verify the old person has access to this machine avl_traverse(&op->person_urls, registry_person_url_callback_verify_machine_exists, &data); if(!data.count) { - registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); + registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); registry_unlock(); return 433; @@ -296,7 +296,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char * data.count = 0; avl_traverse(&np->person_urls, registry_person_url_callback_verify_machine_exists, &data); if(!data.count) { - registry_json_header(w, "switch", REGISTRY_STATUS_FAILED); + registry_json_header(host, w, "switch", REGISTRY_STATUS_FAILED); registry_json_footer(w); registry_unlock(); return 434; @@ -307,7 +307,7 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char * registry_set_person_cookie(w, np); // generate the response - registry_json_header(w, "switch", REGISTRY_STATUS_OK); + registry_json_header(host, w, "switch", REGISTRY_STATUS_OK); buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid); registry_json_footer(w); @@ -323,11 +323,13 @@ void registry_statistics(void) { static RRDSET *sts = NULL, *stc = NULL, *stm = NULL; - if(!sts) sts = rrdset_find("netdata.registry_sessions"); + if(!sts) sts = rrdset_find_localhost("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); + sts = rrdset_create_localhost("netdata", "registry_sessions", NULL, "registry", NULL + , "NetData Registry Sessions", "session", 131000, localhost->rrd_update_every + , RRDSET_TYPE_LINE); - rrddim_add(sts, "sessions", NULL, 1, 1, RRDDIM_ABSOLUTE); + rrddim_add(sts, "sessions", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(sts); @@ -336,15 +338,16 @@ void registry_statistics(void) { // ------------------------------------------------------------------------ - if(!stc) stc = rrdset_find("netdata.registry_entries"); + if(!stc) stc = rrdset_find_localhost("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); + stc = rrdset_create_localhost("netdata", "registry_entries", NULL, "registry", NULL, "NetData Registry Entries" + , "entries", 131100, localhost->rrd_update_every, RRDSET_TYPE_LINE); + + rrddim_add(stc, "persons", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(stc, "machines", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(stc, "urls", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(stc, "persons_urls", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(stc, "machines_urls", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(stc); @@ -357,15 +360,16 @@ void registry_statistics(void) { // ------------------------------------------------------------------------ - if(!stm) stm = rrdset_find("netdata.registry_mem"); + if(!stm) stm = rrdset_find_localhost("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); + stm = rrdset_create_localhost("netdata", "registry_mem", NULL, "registry", NULL, "NetData Registry Memory", "KB" + , 131300, localhost->rrd_update_every, RRDSET_TYPE_STACKED); + + rrddim_add(stm, "persons", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(stm, "machines", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(stm, "urls", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(stm, "persons_urls", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(stm, "machines_urls", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(stm); diff --git a/src/registry.h b/src/registry.h index 4947486c4..2c4592b92 100644 --- a/src/registry.h +++ b/src/registry.h @@ -60,13 +60,16 @@ extern int registry_init(void); extern void registry_free(void); // HTTP requests handled by the registry -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); -extern int registry_request_switch_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when); -extern int registry_request_hello_json(struct web_client *w); +extern int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when); +extern int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); +extern int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when); +extern int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when); +extern int registry_request_hello_json(RRDHOST *host, struct web_client *w); // update the registry monitoring charts extern void registry_statistics(void); +extern char *registry_get_this_machine_guid(void); +extern int regenerate_guid(const char *guid, char *result); + #endif /* NETDATA_REGISTRY_H */ diff --git a/src/registry_init.c b/src/registry_init.c index fb61acd08..2a41d36ee 100644 --- a/src/registry_init.c +++ b/src/registry_init.c @@ -4,45 +4,52 @@ int registry_init(void) { char filename[FILENAME_MAX + 1]; // registry enabled? - registry.enabled = config_get_boolean("registry", "enabled", 0); + if(web_server_mode != WEB_SERVER_MODE_NONE) { + registry.enabled = config_get_boolean(CONFIG_SECTION_REGISTRY, "enabled", 0); + } + else { + info("Registry is disabled - use the central netdata"); + config_set_boolean(CONFIG_SECTION_REGISTRY, "enabled", 0); + registry.enabled = 0; + } // pathnames - registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR "/registry"); + snprintfz(filename, FILENAME_MAX, "%s/registry", netdata_configured_varlib_dir); + registry.pathname = config_get(CONFIG_SECTION_REGISTRY, "registry db directory", filename); 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(); + registry.machine_guid_filename = config_get(CONFIG_SECTION_REGISTRY, "netdata unique id file", filename); snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname); - registry.db_filename = config_get("registry", "registry db file", filename); + registry.db_filename = config_get(CONFIG_SECTION_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); + registry.log_filename = config_get(CONFIG_SECTION_REGISTRY, "registry log file", filename); // configuration options - registry.save_registry_every_entries = (unsigned long long)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", localhost.hostname)); - registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1); + registry.save_registry_every_entries = (unsigned long long)config_get_number(CONFIG_SECTION_REGISTRY, "registry save db every new entries", 1000000); + registry.persons_expiration = config_get_number(CONFIG_SECTION_REGISTRY, "registry expire idle persons days", 365) * 86400; + registry.registry_domain = config_get(CONFIG_SECTION_REGISTRY, "registry domain", ""); + registry.registry_to_announce = config_get(CONFIG_SECTION_REGISTRY, "registry to announce", "https://registry.my-netdata.io"); + registry.hostname = config_get(CONFIG_SECTION_REGISTRY, "registry hostname", config_get(CONFIG_SECTION_GLOBAL, "hostname", "localhost")); + registry.verify_cookies_redirects = config_get_boolean(CONFIG_SECTION_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 = (size_t)config_get_number("registry", "max URL length", 1024); + registry.max_url_length = (size_t)config_get_number(CONFIG_SECTION_REGISTRY, "max URL length", 1024); if(registry.max_url_length < 10) { registry.max_url_length = 10; - config_set_number("registry", "max URL length", (long long)registry.max_url_length); + config_set_number(CONFIG_SECTION_REGISTRY, "max URL length", (long long)registry.max_url_length); } - registry.max_name_length = (size_t)config_get_number("registry", "max URL name length", 50); + registry.max_name_length = (size_t)config_get_number(CONFIG_SECTION_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", (long long)registry.max_name_length); + config_set_number(CONFIG_SECTION_REGISTRY, "max URL name length", (long long)registry.max_name_length); } // initialize entries counters @@ -61,7 +68,7 @@ int registry_init(void) { registry.machines_urls_memory = 0; // initialize locks - pthread_mutex_init(®istry.lock, NULL); + netdata_mutex_init(®istry.lock); // create dictionaries registry.persons = dictionary_create(DICTIONARY_FLAGS); diff --git a/src/registry_internals.c b/src/registry_internals.c index d32d549e2..9ec91ba40 100644 --- a/src/registry_internals.c +++ b/src/registry_internals.c @@ -7,7 +7,7 @@ struct registry registry; // parse a GUID and re-generated to be always lower case // this is used as a protection against the variations of GUIDs -int registry_regenerate_guid(const char *guid, char *result) { +int 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); @@ -18,7 +18,7 @@ int registry_regenerate_guid(const char *guid, char *result) { #ifdef NETDATA_INTERNAL_CHECKS if(strcmp(guid, result)) - info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result); + info("GUID '%s' and re-generated GUID '%s' differ!", guid, result); #endif /* NETDATA_INTERNAL_CHECKS */ } @@ -96,14 +96,14 @@ REGISTRY_PERSON_URL *registry_verify_request(char *person_guid, char *machine_gu url = registry_fix_url(url, NULL); // make sure the person GUID is valid - if(registry_regenerate_guid(person_guid, pbuf) == -1) { + if(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) { + if(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; } @@ -226,7 +226,7 @@ REGISTRY_MACHINE *registry_request_machine(char *person_guid, char *machine_guid if(!pu || !p || !m) return NULL; // make sure the machine GUID is valid - if(registry_regenerate_guid(request_machine, mbuf) == -1) { + if(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; } @@ -275,8 +275,10 @@ static inline int is_machine_guid_blacklisted(const char *guid) { } char *registry_get_this_machine_guid(void) { - if(likely(registry.machine_guid[0])) - return registry.machine_guid; + static char guid[GUID_LEN + 1] = ""; + + if(likely(guid[0])) + return guid; // read it from disk int fd = open(registry.machine_guid_filename, O_RDONLY); @@ -286,38 +288,38 @@ char *registry_get_this_machine_guid(void) { error("Failed to read machine GUID from '%s'", registry.machine_guid_filename); else { buf[GUID_LEN] = '\0'; - if(registry_regenerate_guid(buf, registry.machine_guid) == -1) { + if(regenerate_guid(buf, 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'; + guid[0] = '\0'; } - else if(is_machine_guid_blacklisted(registry.machine_guid)) - registry.machine_guid[0] = '\0'; + else if(is_machine_guid_blacklisted(guid)) + guid[0] = '\0'; } close(fd); } // generate a new one? - if(!registry.machine_guid[0]) { + if(!guid[0]) { uuid_t uuid; uuid_generate_time(uuid); - uuid_unparse_lower(uuid, registry.machine_guid); - registry.machine_guid[GUID_LEN] = '\0'; + uuid_unparse_lower(uuid, guid); + guid[GUID_LEN] = '\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, GUID_LEN) != GUID_LEN) + if(write(fd, guid, GUID_LEN) != GUID_LEN) 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); + setenv("NETDATA_REGISTRY_UNIQUE_ID", guid, 1); - return registry.machine_guid; + return guid; } diff --git a/src/registry_internals.h b/src/registry_internals.h index 9c0b74452..433f04a66 100644 --- a/src/registry_internals.h +++ b/src/registry_internals.h @@ -14,8 +14,6 @@ struct registry { int enabled; - char machine_guid[GUID_LEN + 1]; - // entries counters / statistics unsigned long long persons_count; unsigned long long machines_count; @@ -58,10 +56,10 @@ struct registry { avl_tree registry_urls_root_index; - pthread_mutex_t lock; + netdata_mutex_t lock; }; -extern int registry_regenerate_guid(const char *guid, char *result); +extern int regenerate_guid(const char *guid, char *result); #include "registry_url.h" #include "registry_machine.h" @@ -70,8 +68,6 @@ extern int registry_regenerate_guid(const char *guid, char *result); extern struct registry registry; -extern char *registry_get_this_machine_guid(void); - // REGISTRY LOW-LEVEL REQUESTS (in registry-internals.c) extern REGISTRY_PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when); extern REGISTRY_PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); diff --git a/src/registry_machine.c b/src/registry_machine.c index 3510736df..6dc8200d3 100644 --- a/src/registry_machine.c +++ b/src/registry_machine.c @@ -58,7 +58,7 @@ REGISTRY_MACHINE *registry_machine_get(const char *machine_guid, time_t when) { if(likely(machine_guid && *machine_guid)) { // validate it is a GUID char buf[GUID_LEN + 1]; - if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1)) + if(unlikely(regenerate_guid(machine_guid, buf) == -1)) info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid); else { machine_guid = buf; diff --git a/src/registry_person.c b/src/registry_person.c index 5f9099c9a..409c76925 100644 --- a/src/registry_person.c +++ b/src/registry_person.c @@ -183,7 +183,7 @@ REGISTRY_PERSON *registry_person_get(const char *person_guid, time_t when) { if(person_guid && *person_guid) { char buf[GUID_LEN + 1]; // validate it is a GUID - if(unlikely(registry_regenerate_guid(person_guid, buf) == -1)) + if(unlikely(regenerate_guid(person_guid, buf) == -1)) info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid); else { person_guid = buf; @@ -1,7 +1,6 @@ +#define NETDATA_RRD_INTERNALS 1 #include "common.h" -#define RRD_DEFAULT_GAP_INTERPOLATIONS 1 - // ---------------------------------------------------------------------------- // globals @@ -12,402 +11,124 @@ 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; +int default_rrd_update_every = UPDATE_EVERY; +int default_rrd_history_entries = RRD_DEFAULT_HISTORY_ENTRIES; +RRD_MEMORY_MODE default_rrd_memory_mode = RRD_MEMORY_MODE_SAVE; -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); // ---------------------------------------------------------------------------- -// 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 = { - .next_log_id = 1, - .next_alarm_id = 1, - .count = 0, - .max = 1000, - .alarms = NULL, - .alarm_log_rwlock = PTHREAD_RWLOCK_INITIALIZER - } -}; - -void rrdhost_init(char *hostname) { - localhost.hostname = hostname; - localhost.health_log.next_log_id = - localhost.health_log.next_alarm_id = now_realtime_sec(); -} - -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)); +// RRD - memory modes - 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("RRDFAMILY: INTERNAL ERROR: Expected to INSERT RRDFAMILY '%s' into index, but inserted '%s'.", rc->family, (ret)?ret->family:"NONE"); - } - - rc->use_count++; - return rc; -} +inline const char *rrd_memory_mode_name(RRD_MEMORY_MODE id) { + switch(id) { + case RRD_MEMORY_MODE_RAM: + return RRD_MEMORY_MODE_RAM_NAME; -void rrdfamily_free(RRDFAMILY *rc) { - rc->use_count--; - if(!rc->use_count) { - RRDFAMILY *ret = rrdfamily_index_del(&localhost, rc); - if(ret != rc) - fatal("RRDFAMILY: INTERNAL ERROR: Expected to DELETE RRDFAMILY '%s' from index, but deleted '%s'.", rc->family, (ret)?ret->family:"NONE"); + case RRD_MEMORY_MODE_MAP: + return RRD_MEMORY_MODE_MAP_NAME; - if(rc->variables_root_index.avl_tree.root != NULL) - fatal("RRDFAMILY: INTERNAL ERROR: Variables index of RRDFAMILY '%s' that is freed, is not empty.", rc->family); + case RRD_MEMORY_MODE_NONE: + return RRD_MEMORY_MODE_NONE_NAME; - freez((void *)rc->family); - freez(rc); + case RRD_MEMORY_MODE_SAVE: + default: + return RRD_MEMORY_MODE_SAVE_NAME; } } -// ---------------------------------------------------------------------------- -// RRDSET index +RRD_MEMORY_MODE 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; + else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_NONE_NAME))) + return RRD_MEMORY_MODE_NONE; -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); + return RRD_MEMORY_MODE_SAVE; } -#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(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(&(host->rrdset_root_index), (avl *) &tmp); -} // ---------------------------------------------------------------------------- -// RRDSET name index +// RRD - algorithms types -#define rrdset_from_avlname(avlname_ptr) ((RRDSET *)((avlname_ptr) - offsetof(RRDSET, avlname))) +RRD_ALGORITHM rrd_algorithm_id(const char *name) { + if(strcmp(name, RRD_ALGORITHM_INCREMENTAL_NAME) == 0) + return RRD_ALGORITHM_INCREMENTAL; -static int rrdset_compare_name(void* a, void* b) { - RRDSET *A = rrdset_from_avlname(a); - RRDSET *B = rrdset_from_avlname(b); + else if(strcmp(name, RRD_ALGORITHM_ABSOLUTE_NAME) == 0) + return RRD_ALGORITHM_ABSOLUTE; - // fprintf(stderr, "COMPARING: %s with %s\n", A->name, B->name); + else if(strcmp(name, RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL_NAME) == 0) + return RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL; - 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); -} + else if(strcmp(name, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL_NAME) == 0) + return RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL; -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; + else + return RRD_ALGORITHM_ABSOLUTE; } -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; -} +const char *rrd_algorithm_name(RRD_ALGORITHM algorithm) { + switch(algorithm) { + case RRD_ALGORITHM_ABSOLUTE: + default: + return RRD_ALGORITHM_ABSOLUTE_NAME; -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); + case RRD_ALGORITHM_INCREMENTAL: + return RRD_ALGORITHM_INCREMENTAL_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); + case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL: + return RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL_NAME; - // fprintf(stderr, "FOUND: %s\n", name); - return rrdset_from_avlname(result); + case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL: + return RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL_NAME; } - // fprintf(stderr, "NOT FOUND: %s\n", name); - return NULL; } // ---------------------------------------------------------------------------- -// 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); -} - -#define rrddim_index_add(st, rd) (RRDDIM *)avl_insert_lock(&((st)->dimensions_index), (avl *)(rd)) -#define rrddim_index_del(st,rd ) (RRDDIM *)avl_remove_lock(&((st)->dimensions_index), (avl *)(rd)) +// RRD - chart types -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); +inline RRDSET_TYPE rrdset_type_id(const char *name) { + if(unlikely(strcmp(name, RRDSET_TYPE_AREA_NAME) == 0)) + return RRDSET_TYPE_AREA; - return (RRDDIM *)avl_search_lock(&(st->dimensions_index), (avl *) &tmp); -} - -// ---------------------------------------------------------------------------- -// chart types + else if(unlikely(strcmp(name, RRDSET_TYPE_STACKED_NAME) == 0)) + return RRDSET_TYPE_STACKED; -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; + else // if(unlikely(strcmp(name, RRDSET_TYPE_LINE_NAME) == 0)) + 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; - +const char *rrdset_type_name(RRDSET_TYPE chart_type) { switch(chart_type) { case RRDSET_TYPE_LINE: - return line; + default: + return RRDSET_TYPE_LINE_NAME; case RRDSET_TYPE_AREA: - return area; + return RRDSET_TYPE_AREA_NAME; case RRDSET_TYPE_STACKED: - return stacked; + return RRDSET_TYPE_STACKED_NAME; } - return line; -} - -// ---------------------------------------------------------------------------- -// load / save - -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; - - switch(id) { - case RRD_MEMORY_MODE_RAM: - return ram; - - case RRD_MEMORY_MODE_MAP: - return map; - - case RRD_MEMORY_MODE_SAVE: - default: - 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; - - return RRD_MEMORY_MODE_SAVE; } -// ---------------------------------------------------------------------------- -// algorithms types - -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; -} - -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; - - switch(chart_type) { - case RRDDIM_ABSOLUTE: - return absolute; - - case RRDDIM_INCREMENTAL: - return incremental; - - case RRDDIM_PCENT_OVER_ROW_TOTAL: - return percentage_of_absolute_row; - - case RRDDIM_PCENT_OVER_DIFF_TOTAL: - return percentage_of_incremental_row; - } - return absolute; -} // ---------------------------------------------------------------------------- -// chart names +// RRD - cache directory -char *rrdset_strncpyz_name(char *to, const char *from, size_t length) -{ - 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) -{ - if(unlikely(st->name && !strcmp(st->name, name))) - return; - - debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name); - - 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_strncpyz_name(b, n, CONFIG_MAX_VALUE); - - if(st->name) { - rrdset_index_del_name(&localhost, st); - st->name = config_set_default(st->id, "name", b); - st->hash_name = simple_hash(st->name); - rrdsetvar_rename_all(st); - } - else { - st->name = config_get(st->id, "name", b); - st->hash_name = simple_hash(st->name); - } - - pthread_rwlock_wrlock(&st->rwlock); - RRDDIM *rd; - for(rd = st->dimensions; rd ;rd = rd->next) - rrddimvar_rename_all(rd); - pthread_rwlock_unlock(&st->rwlock); - - if(unlikely(rrdset_index_add_name(&localhost, st) != st)) - error("RRDSET: INTERNAL ERROR: attempted to index duplicate chart name '%s'", st->name); -} - -// ---------------------------------------------------------------------------- -// cache directory - -char *rrdset_cache_dir(const char *id) -{ +char *rrdset_cache_dir(RRDHOST *host, const char *id, const char *config_section) { 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); + snprintfz(n, FILENAME_MAX, "%s/%s", host->cache_dir, b); + ret = config_get(config_section, "cache directory", n); - if(rrd_memory_mode == RRD_MEMORY_MODE_MAP || rrd_memory_mode == RRD_MEMORY_MODE_SAVE) { + if(host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) { int r = mkdir(ret, 0775); if(r != 0 && errno != EEXIST) error("Cannot create directory '%s'", ret); @@ -415,1236 +136,3 @@ char *rrdset_cache_dir(const char *id) return ret; } - -// ---------------------------------------------------------------------------- -// core functions - -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; - memset(rd->values, 0, rd->entries * sizeof(storage_number)); - } -} -static inline long align_entries_to_pagesize(long entries) { - if(entries < 5) entries = 5; - if(entries > RRD_HISTORY_ENTRIES_MAX) entries = RRD_HISTORY_ENTRIES_MAX; - -#ifdef NETDATA_LOG_ALLOCATIONS - long page = (size_t)sysconf(_SC_PAGESIZE); - - long size = sizeof(RRDDIM) + entries * sizeof(storage_number); - if(size % page) { - size -= (size % page); - size += page; - - long n = (size - sizeof(RRDDIM)) / sizeof(storage_number); - return n; - } - - return entries; -#else - return entries; -#endif -} - -static inline void timeval_align(struct timeval *tv, int update_every) { - tv->tv_sec -= tv->tv_sec % update_every; - tv->tv_usec = 500000; -} - -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]; - - snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id); - - RRDSET *st = rrdset_find(fullid); - if(st) { - error("Cannot create rrd stats for '%s', it already exists.", fullid); - return st; - } - - long rentries = config_get_number(fullid, "history", rrd_default_history_entries); - long entries = align_entries_to_pagesize(rentries); - if(entries != rentries) entries = config_set_number(fullid, "history", entries); - - 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); - memset(st, 0, 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; - memset(st, 0, size); - } - else if(st->memsize != size || st->entries != entries) { - errno = 0; - error("File %s does not have the desired size. Clearing it.", fullfilename); - memset(st, 0, size); - } - else if(st->update_every != update_every) { - errno = 0; - error("File %s does not have the desired update frequency. Clearing it.", fullfilename); - memset(st, 0, size); - } - else if((now_realtime_sec() - st->last_updated.tv_sec) > update_every * entries) { - errno = 0; - error("File %s is too old. Clearing it.", fullfilename); - memset(st, 0, size); - } - - // make sure the database is aligned - if(st->last_updated.tv_sec) - timeval_align(&st->last_updated, update_every); - } - - 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; - st->alarms = NULL; - memset(&st->rwlock, 0, sizeof(pthread_rwlock_t)); - memset(&st->avl, 0, sizeof(avl)); - memset(&st->avlname, 0, sizeof(avl)); - memset(&st->variables_root_index, 0, sizeof(avl_tree_lock)); - memset(&st->dimensions_index, 0, sizeof(avl_tree_lock)); - } - 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]; - char varvalue2[CONFIG_MAX_VALUE + 1]; - snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name); - json_escape_string(varvalue2, varvalue, sizeof(varvalue2)); - st->title = config_get(st->id, "title", varvalue2); - } - - 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); - } - - if(unlikely(rrdset_index_add(&localhost, st) != st)) - error("RRDSET: INTERNAL ERROR: attempt to index duplicate chart '%s'", st->id); - - 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) -{ - RRDDIM *rd = rrddim_find(st, id); - if(rd) { - debug(D_RRD_CALLS, "Cannot create rrd dimension '%s/%s', it already exists.", st->id, name?name:"<NONAME>"); - return rd; - } - - char filename[FILENAME_MAX + 1]; - char fullfilename[FILENAME_MAX + 1]; - - char varname[CONFIG_MAX_NAME + 1]; - 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; - now_realtime_timeval(&now); - - if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) { - errno = 0; - info("Initializing file %s.", fullfilename); - memset(rd, 0, size); - } - else if(rd->memsize != size) { - errno = 0; - error("File %s does not have the desired size. Clearing it.", fullfilename); - memset(rd, 0, size); - } - else if(rd->multiplier != multiplier) { - errno = 0; - error("File %s does not have the same multiplier. Clearing it.", fullfilename); - memset(rd, 0, size); - } - else if(rd->divisor != divisor) { - errno = 0; - error("File %s does not have the same divisor. Clearing it.", fullfilename); - memset(rd, 0, size); - } - else if(rd->algorithm != algorithm) { - errno = 0; - error("File %s does not have the same algorithm. Clearing it.", fullfilename); - memset(rd, 0, size); - } - else if(rd->update_every != st->update_every) { - errno = 0; - error("File %s does not have the same refresh frequency. Clearing it.", fullfilename); - memset(rd, 0, size); - } - else if(dt_usec(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * USEC_PER_SEC)) { - errno = 0; - error("File %s is too old. Clearing it.", fullfilename); - memset(rd, 0, 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; - memset(rd, 0, 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; - memset(&rd->avl, 0, sizeof(avl)); - } - 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); - - if(unlikely(rrddim_index_add(st, rd) != rd)) - error("RRDDIM: INTERNAL ERROR: attempt to index duplicate dimension '%s' on chart '%s'", rd->id, st->id); - - return(rd); -} - -void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name) -{ - if(unlikely(rd->name && !strcmp(rd->name, name))) - return; - - debug(D_RRD_CALLS, "rrddim_set_name() from %s.%s to %s.%s", st->name, rd->name, st->name, name); - - char varname[CONFIG_MAX_NAME + 1]; - snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id); - rd->name = 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); - - 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); - - if(unlikely(rrddim_index_del(st, rd) != rd)) - error("RRDDIM: INTERNAL ERROR: attempt to remove from index dimension '%s' on chart '%s', removed a different dimension.", rd->id, st->id); - - // 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..."); - - rrdhost_rwlock(&localhost); - - RRDSET *st; - for(st = localhost.rrdset_root; st ;) { - RRDSET *next = st->next; - - pthread_rwlock_wrlock(&st->rwlock); - - while(st->variables) - rrdsetvar_free(st->variables); - - while(st->alarms) - rrdsetcalc_unlink(st->alarms); - - while(st->dimensions) - rrddim_free(st, st->dimensions); - - if(unlikely(rrdset_index_del(&localhost, st) != st)) - error("RRDSET: INTERNAL ERROR: attempt to remove from index chart '%s', removed a different chart.", st->id); - - rrdset_index_del_name(&localhost, st); - - 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 || 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..."); -} - -void rrdset_save_all(void) { - info("Saving database..."); - - RRDSET *st; - RRDDIM *rd; - - // we get an write lock - // to ensure only one thread is saving the database - rrdhost_rwlock(&localhost); - - for(st = localhost.rrdset_root; st ; st = st->next) { - pthread_rwlock_rdlock(&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); - } - - 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); - } - - rrdhost_unlock(&localhost); -} - - -RRDSET *rrdset_find(const char *id) -{ - debug(D_RRD_CALLS, "rrdset_find() for chart %s", id); - - 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); - - 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)); - - return(rrdset_find(buf)); -} - -RRDSET *rrdset_find_byname(const char *name) -{ - debug(D_RRD_CALLS, "rrdset_find_byname() for chart %s", name); - - 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); - - 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); - - 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; -} - -int rrddim_unhide(RRDSET *st, const char *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; - } - - 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); - - now_realtime_timeval(&rd->last_collected_time); - rd->collected_value = value; - rd->updated = 1; - rd->counter++; - - // fprintf(stderr, "%s.%s %llu " COLLECTED_NUMBER_FORMAT " dt %0.6f" " rate " CALCULATED_NUMBER_FORMAT "\n", st->name, rd->name, st->usec_since_last_update, value, (float)((double)st->usec_since_last_update / (double)1000000), (calculated_number)((value - rd->last_collected_value) * (calculated_number)rd->multiplier / (calculated_number)rd->divisor * 1000000.0 / (calculated_number)st->usec_since_last_update)); - - 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; - } - - return rrddim_set_by_pointer(st, rd, value); -} - -void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds) -{ - if(unlikely(!st->last_collected_time.tv_sec || !microseconds)) { - // the first entry - microseconds = st->update_every * USEC_PER_SEC; - } - st->usec_since_last_update = microseconds; -} - -void rrdset_next_usec(RRDSET *st, usec_t microseconds) -{ - struct timeval now; - now_realtime_timeval(&now); - - if(unlikely(!st->last_collected_time.tv_sec)) { - // the first entry - microseconds = st->update_every * USEC_PER_SEC; - } - else if(unlikely(!microseconds)) { - // no dt given by the plugin - microseconds = dt_usec(&now, &st->last_collected_time); - } - else { - // microseconds has the time since the last collection - usec_t now_usec = timeval_usec(&now); - usec_t last_usec = timeval_usec(&st->last_collected_time); - usec_t since_last_usec = dt_usec(&now, &st->last_collected_time); - - // verify the microseconds given is good - if(unlikely(microseconds > since_last_usec)) { - debug(D_RRD_CALLS, "dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - since_last_usec, st->name, st->id); - -#ifdef NETDATA_INTERNAL_CHECKS - if(unlikely(last_usec + microseconds > now_usec + 1000)) - error("dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - since_last_usec, st->name, st->id); -#endif - - microseconds = since_last_usec; - } - else if(unlikely(microseconds < since_last_usec * 0.8)) { - debug(D_RRD_CALLS, "dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, since_last_usec, st->name, st->id); - -#ifdef NETDATA_INTERNAL_CHECKS - error("dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, since_last_usec, st->name, st->id); -#endif - microseconds = since_last_usec; - } - } - 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; -} - -usec_t rrdset_done(RRDSET *st) -{ - if(unlikely(netdata_exit)) return 0; - - debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name); - - RRDDIM *rd; - - int - pthreadoldcancelstate; // store the old cancelable pthread state, to restore it at the end - - char - store_this_entry = 1, // boolean: 1 = store this entry, 0 = don't store this entry - first_entry = 0; // boolean: 1 = this is the first entry seen for this chart, 0 = all other entries - - unsigned int - stored_entries = 0; // the number of entries we have stored in the db, during this call to rrdset_done() - - usec_t - last_collect_ut, // the timestamp in microseconds, of the last collected value - now_collect_ut, // the timestamp in microseconds, of this collected value (this is NOW) - last_stored_ut, // the timestamp in microseconds, of the last stored entry in the db - next_store_ut, // the timestamp in microseconds, of the next entry to store in the db - update_every_ut = st->update_every * USEC_PER_SEC; // st->update_every in microseconds - - if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 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 * update_every_ut)) { - info("%s: took too long to be updated (%0.3Lf secs). Resetting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0)); - rrdset_reset(st); - st->usec_since_last_update = update_every_ut; - 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 - now_realtime_timeval(&st->last_collected_time); - timeval_align(&st->last_collected_time, st->update_every); - - last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - update_every_ut; - - // 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 - last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec; - usec_t ut = last_collect_ut + st->usec_since_last_update; - st->last_collected_time.tv_sec = (time_t) (ut / USEC_PER_SEC); - st->last_collected_time.tv_usec = (suseconds_t) (ut % USEC_PER_SEC); - } - - // 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 - usec_t ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - st->usec_since_last_update; - st->last_updated.tv_sec = (time_t) (ut / USEC_PER_SEC); - st->last_updated.tv_usec = (suseconds_t) (ut % USEC_PER_SEC); - - // 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(dt_usec(&st->last_collected_time, &st->last_updated) > st->entries * update_every_ut)) { - info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Resetting 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 = update_every_ut; - - now_realtime_timeval(&st->last_collected_time); - timeval_align(&st->last_collected_time, st->update_every); - - usec_t ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - st->usec_since_last_update; - st->last_updated.tv_sec = (time_t) (ut / USEC_PER_SEC); - st->last_updated.tv_usec = (suseconds_t) (ut % USEC_PER_SEC); - - // 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_stored_ut = the last time we added a value to the storage - // now_collect_ut = the time the current value has been collected - // next_store_ut = the time of the next interpolation point - last_stored_ut = st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec; - now_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec; - next_store_ut = (st->last_updated.tv_sec + st->update_every) * USEC_PER_SEC; - - if(unlikely(st->debug)) { - debug(D_RRD_STATS, "%s: last_collect_ut = %0.3Lf (last collection time)", st->name, (long double)last_collect_ut/1000000.0); - debug(D_RRD_STATS, "%s: now_collect_ut = %0.3Lf (current collection time)", st->name, (long double)now_collect_ut/1000000.0); - debug(D_RRD_STATS, "%s: last_stored_ut = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0); - debug(D_RRD_STATS, "%s: next_store_ut = %0.3Lf (next interpolation point)", st->name, (long double)next_store_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 ; 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 ; 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 - - if(unlikely(now_collect_ut < next_store_ut)) { - // this is collected in the same interpolation point - if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name); -#ifdef NETDATA_INTERNAL_CHECKS - info("%s is collected in the same interpolation point: short by %llu microseconds", st->name, next_store_ut - now_collect_ut); -#endif - } - - usec_t first_ut = last_stored_ut; - long long iterations = (now_collect_ut - last_stored_ut) / (update_every_ut); - if((now_collect_ut % (update_every_ut)) == 0) iterations++; - - for( ; next_store_ut <= now_collect_ut ; last_collect_ut = next_store_ut, next_store_ut += update_every_ut, iterations-- ) { -#ifdef NETDATA_INTERNAL_CHECKS - if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_stored_ut = %llu, next_store_ut = %llu, now_collect_ut = %llu", st->name, first_ut, last_stored_ut, next_store_ut, now_collect_ut); } -#endif - - if(unlikely(st->debug)) { - debug(D_RRD_STATS, "%s: last_stored_ut = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0); - debug(D_RRD_STATS, "%s: next_store_ut = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0); - } - - st->last_updated.tv_sec = (time_t) (next_store_ut / USEC_PER_SEC); - 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_store_ut - last_collect_ut) - / (calculated_number)(now_collect_ut - last_collect_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_store_ut - last_stored_ut) - , (now_collect_ut - last_stored_ut) - ); - - rd->calculated_value -= new_value; - new_value += rd->last_calculated_value; - rd->last_calculated_value = 0; - new_value /= (calculated_number)st->update_every; - - if(unlikely(next_store_ut - last_stored_ut < update_every_ut)) { - if(unlikely(st->debug)) - debug(D_RRD_STATS, "%s/%s: COLLECTION POINT IS SHORT " CALCULATED_NUMBER_FORMAT " - EXTRAPOLATING", - st->id, rd->name - , (calculated_number)(next_store_ut - last_stored_ut) - ); - new_value = new_value * (calculated_number)(st->update_every * 1000000) / (calculated_number)(next_store_ut - last_stored_ut); - } - 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_store_ut - last_collect_ut) - / (calculated_number)(now_collect_ut - last_collect_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_store_ut - first_ut) - , (now_collect_ut - first_ut), rd->last_calculated_value - ); - } - break; - } - - if(unlikely(!store_this_entry)) { - rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS); - 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_stored_ut = next_store_ut; - } - - st->last_collected_total = st->collected_total; - - for( rd = st->dimensions; rd ; rd = rd->next ) { - if(unlikely(!rd->updated)) continue; - - 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; - - switch(rd->algorithm) { - case RRDDIM_INCREMENTAL: - if(unlikely(!first_entry)) { - 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->calculated_value); - rd->last_calculated_value += rd->calculated_value; - } - else { - if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS THE FIRST POINT", st->name); - } - break; - - case RRDDIM_ABSOLUTE: - case RRDDIM_PCENT_OVER_ROW_TOTAL: - case RRDDIM_PCENT_OVER_DIFF_TOTAL: - 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; - break; - } - - 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)) { - RRDDIM *last; - // 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(pthreadoldcancelstate, NULL) != 0)) - error("Cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate); - - return(st->usec_since_last_update); -} @@ -3,20 +3,17 @@ #define UPDATE_EVERY 1 #define UPDATE_EVERY_MAX 3600 -extern int rrd_update_every; #define RRD_DEFAULT_HISTORY_ENTRIES 3600 #define RRD_HISTORY_ENTRIES_MAX (86400*10) -extern int rrd_default_history_entries; -// time in seconds to delete unupdated dimensions -// set to zero to disable this feature -extern int rrd_delete_unupdated_dimensions; +extern int default_rrd_update_every; +extern int default_rrd_history_entries; -#define RRD_ID_LENGTH_MAX 400 +#define RRD_ID_LENGTH_MAX 200 -#define RRDSET_MAGIC "NETDATA RRD SET FILE V018" -#define RRDDIMENSION_MAGIC "NETDATA RRD DIMENSION FILE V018" +#define RRDSET_MAGIC "NETDATA RRD SET FILE V019" +#define RRDDIMENSION_MAGIC "NETDATA RRD DIMENSION FILE V019" typedef long long total_number; #define TOTAL_NUMBER_FORMAT "%lld" @@ -24,59 +21,61 @@ typedef long long total_number; // ---------------------------------------------------------------------------- // chart types +typedef enum rrdset_type { + RRDSET_TYPE_LINE = 0, + RRDSET_TYPE_AREA = 1, + RRDSET_TYPE_STACKED = 2 +} RRDSET_TYPE; + #define RRDSET_TYPE_LINE_NAME "line" #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_STACKED 2 - -int rrdset_type_id(const char *name); -const char *rrdset_type_name(int chart_type); +RRDSET_TYPE rrdset_type_id(const char *name); +const char *rrdset_type_name(RRDSET_TYPE chart_type); // ---------------------------------------------------------------------------- // memory mode +typedef enum rrd_memory_mode { + RRD_MEMORY_MODE_NONE = 0, + RRD_MEMORY_MODE_RAM = 1, + RRD_MEMORY_MODE_MAP = 2, + RRD_MEMORY_MODE_SAVE = 3 +} RRD_MEMORY_MODE; + +#define RRD_MEMORY_MODE_NONE_NAME "none" #define RRD_MEMORY_MODE_RAM_NAME "ram" #define RRD_MEMORY_MODE_MAP_NAME "map" #define RRD_MEMORY_MODE_SAVE_NAME "save" -#define RRD_MEMORY_MODE_RAM 0 -#define RRD_MEMORY_MODE_MAP 1 -#define RRD_MEMORY_MODE_SAVE 2 - -extern int rrd_memory_mode; +extern RRD_MEMORY_MODE default_rrd_memory_mode; -extern const char *rrd_memory_mode_name(int id); -extern int rrd_memory_mode_id(const char *name); +extern const char *rrd_memory_mode_name(RRD_MEMORY_MODE id); +extern RRD_MEMORY_MODE 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" +typedef enum rrd_algorithm { + RRD_ALGORITHM_ABSOLUTE = 0, + RRD_ALGORITHM_INCREMENTAL = 1, + RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL = 2, + RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL = 3 +} RRD_ALGORITHM; -#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); - -// ---------------------------------------------------------------------------- -// flags +#define RRD_ALGORITHM_ABSOLUTE_NAME "absolute" +#define RRD_ALGORITHM_INCREMENTAL_NAME "incremental" +#define RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL_NAME "percentage-of-incremental-row" +#define RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL_NAME "percentage-of-absolute-row" -#define RRDDIM_FLAG_HIDDEN 0x00000001 // this dimension will not be offered to callers -#define RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS 0x00000002 // do not offer RESET or OVERFLOW info to callers +extern RRD_ALGORITHM rrd_algorithm_id(const char *name); +extern const char *rrd_algorithm_name(RRD_ALGORITHM algorithm); // ---------------------------------------------------------------------------- -// RRD CONTEXT +// RRD FAMILY struct rrdfamily { avl avl; @@ -90,8 +89,25 @@ struct rrdfamily { }; typedef struct rrdfamily RRDFAMILY; + +// ---------------------------------------------------------------------------- +// flags +// use this for configuration flags, not for state control +// flags are set/unset in a manner that is not thread safe +// and may lead to missing information. + +typedef enum rrddim_flags { + RRDDIM_FLAG_HIDDEN = 1 << 0, // this dimension will not be offered to callers + RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS = 1 << 1 // do not offer RESET or OVERFLOW info to callers +} RRDDIM_FLAGS; + +#define rrddim_flag_check(rd, flag) ((rd)->flags & flag) +#define rrddim_flag_set(rd, flag) (rd)->flags |= flag +#define rrddim_flag_clear(rd, flag) (rd)->flags &= ~flag + + // ---------------------------------------------------------------------------- -// RRD DIMENSION +// RRD DIMENSION - this is a metric struct rrddim { // ------------------------------------------------------------------------ @@ -102,18 +118,20 @@ struct rrddim { // ------------------------------------------------------------------------ // the dimension definition - char id[RRD_ID_LENGTH_MAX + 1]; // the id of this dimension (for internal identification) - + const char *id; // the id of this dimension (for internal identification) 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) + // DO NOT FREE THIS - IT IS ALLOCATED IN CONFIG + + RRD_ALGORITHM algorithm; // the algorithm that is applied to add new collected values + RRD_MEMORY_MODE rrd_memory_mode; // the memory mode for this dimension - 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 + collected_number multiplier; // the multiplier of the collected values + collected_number divisor; // the divider of the collected values - int mapped; // if set to non zero, this dimension is mapped to a file + uint32_t flags; // configuration flags for the dimension // ------------------------------------------------------------------------ // members for temporary data we need for calculations @@ -122,17 +140,15 @@ struct rrddim { // instead of strcmp() every item in the binary index // we first compare the hashes - // FIXME - // we need the hash_name too! + uint32_t hash_name; // a simple hash of the name - uint32_t flags; + char *cache_filename; // the filename we load/save from/to this set - char cache_filename[FILENAME_MAX+1]; // the filename we load/save from/to this set + size_t collections_counter; // the number of times we added values to this rrdim + size_t unused[10]; - unsigned long counter; // the number of times we added values to this rrdim - - 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 + int updated:1; // 1 when the dimension has been updated since the last processing + int exposed:1; // 1 when set what have sent this dimension to the central netdata struct timeval last_collected_time; // when was this dimension last updated // this is actual date time we updated the last_collected_value @@ -163,7 +179,7 @@ struct rrddim { int update_every; // every how many seconds is this updated - unsigned long memsize; // the memory allocated for this dimension + size_t memsize; // the memory allocated for this dimension char magic[sizeof(RRDDIMENSION_MAGIC) + 1]; // a string to be saved, used to identify our data file @@ -176,9 +192,34 @@ struct rrddim { }; typedef struct rrddim RRDDIM; +// ---------------------------------------------------------------------------- +// these loop macros make sure the linked list is accessed with the right lock + +#define rrddim_foreach_read(rd, st) \ + for(rd = st->dimensions, rrdset_check_rdlock(st); rd ; rd = rd->next) + +#define rrddim_foreach_write(rd, st) \ + for(rd = st->dimensions, rrdset_check_wrlock(st); rd ; rd = rd->next) + // ---------------------------------------------------------------------------- -// RRDSET +// RRDSET - this is a chart + +// use this for configuration flags, not for state control +// flags are set/unset in a manner that is not thread safe +// and may lead to missing information. + +typedef enum rrdset_flags { + RRDSET_FLAG_ENABLED = 1 << 0, // enables or disables a chart + RRDSET_FLAG_DETAIL = 1 << 1, // 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) + RRDSET_FLAG_DEBUG = 1 << 2, // enables or disables debugging for a chart + RRDSET_FLAG_OBSOLETE = 1 << 3 // this is marked by the collector/module as obsolete +} RRDSET_FLAGS; + +#define rrdset_flag_check(st, flag) ((st)->flags & flag) +#define rrdset_flag_set(st, flag) (st)->flags |= flag +#define rrdset_flag_clear(st, flag) (st)->flags &= ~flag struct rrdset { // ------------------------------------------------------------------------ @@ -197,6 +238,8 @@ struct rrdset { // since the config always has a higher priority // (the user overwrites the name of the charts) + char *config_section; // the config section for the chart + 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 @@ -205,7 +248,7 @@ struct rrdset { char *context; // the template of this data set uint32_t hash_context; - int chart_type; + RRDSET_TYPE chart_type; int update_every; // every how many seconds is this updated? @@ -214,30 +257,29 @@ struct rrdset { long current_entry; // the entry that is currently being updated // it goes around in a round-robin fashion - int enabled; + uint32_t flags; // configuration flags 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 long priority; - 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) // ------------------------------------------------------------------------ // members for temporary data we need for calculations - int mapped; // if set to 1, this is memory mapped - - int debug; + RRD_MEMORY_MODE rrd_memory_mode; // 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 - pthread_rwlock_t rwlock; + netdata_rwlock_t rrdset_rwlock; // protects dimensions linked list + + size_t counter; // the number of times we added values to this database + size_t counter_done; // the number of times rrdset_done() has been called - 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 + time_t last_accessed_time; // the last time this RRDSET has been accessed + size_t unused[9]; 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 @@ -283,83 +325,248 @@ struct rrdset { }; typedef struct rrdset RRDSET; +#define rrdset_rdlock(st) netdata_rwlock_rdlock(&((st)->rrdset_rwlock)) +#define rrdset_wrlock(st) netdata_rwlock_wrlock(&((st)->rrdset_rwlock)) +#define rrdset_unlock(st) netdata_rwlock_unlock(&((st)->rrdset_rwlock)) + + +// ---------------------------------------------------------------------------- +// these loop macros make sure the linked list is accessed with the right lock + +#define rrdset_foreach_read(st, host) \ + for(st = host->rrdset_root, rrdhost_check_rdlock(host); st ; st = st->next) + +#define rrdset_foreach_write(st, host) \ + for(st = host->rrdset_root, rrdhost_check_wrlock(host); st ; st = st->next) + + +// ---------------------------------------------------------------------------- +// RRDHOST flags +// use this for configuration flags, not for state control +// flags are set/unset in a manner that is not thread safe +// and may lead to missing information. + +typedef enum rrdhost_flags { + RRDHOST_ORPHAN = 1 << 0, // this host is orphan + RRDHOST_DELETE_OBSOLETE_FILES = 1 << 1, // delete files of obsolete charts + RRDHOST_DELETE_ORPHAN_FILES = 1 << 2 // delete the entire host when orphan +} RRDHOST_FLAGS; + +#define rrdhost_flag_check(host, flag) ((host)->flags & flag) +#define rrdhost_flag_set(host, flag) (host)->flags |= flag +#define rrdhost_flag_clear(host, flag) (host)->flags &= ~flag + // ---------------------------------------------------------------------------- // RRD HOST struct rrdhost { - avl avl; + avl avl; // the index of hosts - char *hostname; + // ------------------------------------------------------------------------ + // host information - RRDSET *rrdset_root; - pthread_rwlock_t rrdset_root_rwlock; + char *hostname; // the hostname of this host + uint32_t hash_hostname; // the hostname hash - avl_tree_lock rrdset_root_index; - avl_tree_lock rrdset_root_index_name; + char machine_guid[GUID_LEN + 1]; // the unique ID of this host + uint32_t hash_machine_guid; // the hash of the unique ID - avl_tree_lock rrdfamily_root_index; - avl_tree_lock variables_root_index; + char *os; // the O/S type of the host + + uint32_t flags; // flags about this RRDHOST + + int rrd_update_every; // the update frequency of the host + long rrd_history_entries; // the number of history entries for the host's charts + RRD_MEMORY_MODE rrd_memory_mode; // the memory more for the charts of this host + + char *cache_dir; // the directory to save RRD cache files + char *varlib_dir; // the directory to save health log + + + // ------------------------------------------------------------------------ + // streaming of data to remote hosts - rrdpush + + int rrdpush_enabled:1; // 1 when this host sends metrics to another netdata + char *rrdpush_destination; // where to send metrics to + char *rrdpush_api_key; // the api key at the receiving netdata + volatile int rrdpush_connected:1; // 1 when the sender is ready to push metrics + volatile int rrdpush_spawn:1; // 1 when the sender thread has been spawn + volatile int rrdpush_error_shown:1; // 1 when we have logged a communication error + int rrdpush_socket; // the fd of the socket to the remote host, or -1 + pthread_t rrdpush_thread; // the sender thread + netdata_mutex_t rrdpush_mutex; // exclusive access to rrdpush_buffer + int rrdpush_pipe[2]; // collector to sender thread communication + BUFFER *rrdpush_buffer; // collector fills it, sender sends them + + + // ------------------------------------------------------------------------ + // streaming of data from remote hosts - rrdpush + + volatile size_t connected_senders; // when remote hosts are streaming to this + // host, this is the counter of connected clients + + time_t senders_disconnected_time; // the time the last sender was disconnected + + // ------------------------------------------------------------------------ + // health monitoring options + + int health_enabled:1; // 1 when this host has health enabled + time_t health_delay_up_to; // a timestamp to delay alarms processing up to + char *health_default_exec; // the full path of the alarms notifications program + char *health_default_recipient; // the default recipient for all alarms + char *health_log_filename; // the alarms event log filename + size_t health_log_entries_written; // the number of alarm events writtern to the alarms event log + FILE *health_log_fp; // the FILE pointer to the open alarms event log file // 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; + ALARM_LOG health_log; // alarms historical events (event log) + + // templates of alarms + // these are used to create alarms when charts + // are created or renamed, that match them RRDCALCTEMPLATE *templates; + + + // ------------------------------------------------------------------------ + // the charts of the host + + RRDSET *rrdset_root; // the host charts + + + // ------------------------------------------------------------------------ + // locks + + netdata_rwlock_t rrdhost_rwlock; // lock for this RRDHOST (protects rrdset_root linked list) + + avl_tree_lock rrdset_root_index; // the host's charts index (by id) + avl_tree_lock rrdset_root_index_name; // the host's charts index (by name) + + avl_tree_lock rrdfamily_root_index; // the host's chart families index + avl_tree_lock variables_root_index; // the host's chart variables index + + struct rrdhost *next; }; typedef struct rrdhost RRDHOST; -extern RRDHOST localhost; -extern void rrdhost_init(char *hostname); +extern RRDHOST *localhost; + +#define rrdhost_rdlock(host) netdata_rwlock_rdlock(&((host)->rrdhost_rwlock)) +#define rrdhost_wrlock(host) netdata_rwlock_wrlock(&((host)->rrdhost_rwlock)) +#define rrdhost_unlock(host) netdata_rwlock_unlock(&((host)->rrdhost_rwlock)) + +// ---------------------------------------------------------------------------- +// these loop macros make sure the linked list is accessed with the right lock + +#define rrdhost_foreach_read(var) \ + for(var = localhost, rrd_check_rdlock(); var ; var = var->next) + +#define rrdhost_foreach_write(var) \ + for(var = localhost, rrd_check_wrlock(); var ; var = var->next) + + +// ---------------------------------------------------------------------------- +// global lock for all RRDHOSTs + +extern netdata_rwlock_t rrd_rwlock; + +#define rrd_rdlock() netdata_rwlock_rdlock(&rrd_rwlock) +#define rrd_wrlock() netdata_rwlock_wrlock(&rrd_rwlock) +#define rrd_unlock() netdata_rwlock_unlock(&rrd_rwlock) + +// ---------------------------------------------------------------------------- + +extern size_t rrd_hosts_available; +extern time_t rrdhost_free_orphan_time; + +extern void rrd_init(char *hostname); + +extern RRDHOST *rrdhost_find_by_hostname(const char *hostname, uint32_t hash); +extern RRDHOST *rrdhost_find_by_guid(const char *guid, uint32_t hash); + +extern RRDHOST *rrdhost_find_or_create( + const char *hostname + , const char *guid + , const char *os + , int update_every + , long history + , RRD_MEMORY_MODE mode + , int health_enabled + , int rrdpush_enabled + , char *rrdpush_destination + , char *rrdpush_api_key +); + +#if defined(NETDATA_INTERNAL_CHECKS) && defined(NETDATA_VERIFY_LOCKS) +extern void __rrdhost_check_wrlock(RRDHOST *host, const char *file, const char *function, const unsigned long line); +extern void __rrdhost_check_rdlock(RRDHOST *host, const char *file, const char *function, const unsigned long line); +extern void __rrdset_check_rdlock(RRDSET *st, const char *file, const char *function, const unsigned long line); +extern void __rrdset_check_wrlock(RRDSET *st, const char *file, const char *function, const unsigned long line); +extern void __rrd_check_rdlock(const char *file, const char *function, const unsigned long line); +extern void __rrd_check_wrlock(const char *file, const char *function, const unsigned long line); + +#define rrdhost_check_rdlock(host) __rrdhost_check_rdlock(host, __FILE__, __FUNCTION__, __LINE__) +#define rrdhost_check_wrlock(host) __rrdhost_check_wrlock(host, __FILE__, __FUNCTION__, __LINE__) +#define rrdset_check_rdlock(st) __rrdset_check_rdlock(st, __FILE__, __FUNCTION__, __LINE__) +#define rrdset_check_wrlock(st) __rrdset_check_wrlock(st, __FILE__, __FUNCTION__, __LINE__) +#define rrd_check_rdlock() __rrd_check_rdlock(__FILE__, __FUNCTION__, __LINE__) +#define rrd_check_wrlock() __rrd_check_wrlock(__FILE__, __FUNCTION__, __LINE__) -#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 +#define rrdset_check_rdlock(host) (void)0 +#define rrdset_check_wrlock(host) (void)0 +#define rrd_check_rdlock() (void)0 +#define rrd_check_wrlock() (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 +// RRDSET functions -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); +extern RRDSET *rrdset_create(RRDHOST *host + , 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 + , RRDSET_TYPE chart_type); -extern void rrdset_reset(RRDSET *st); +#define rrdset_create_localhost(type, id, name, family, context, title, units, priority, update_every, chart_type) rrdset_create(localhost, type, id, name, family, context, title, units, priority, update_every, chart_type) -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); +extern void rrdhost_free_all(void); +extern void rrdhost_save_all(void); + +extern void rrdhost_cleanup_orphan(RRDHOST *protected); +extern void rrdhost_free(RRDHOST *host); +extern void rrdhost_save(RRDHOST *host); +extern void rrdhost_delete(RRDHOST *host); + +extern RRDSET *rrdset_find(RRDHOST *host, const char *id); +#define rrdset_find_localhost(id) rrdset_find(localhost, id) -extern void rrdset_free_all(void); -extern void rrdset_save_all(void); +extern RRDSET *rrdset_find_bytype(RRDHOST *host, const char *type, const char *id); +#define rrdset_find_bytype_localhost(type, id) rrdset_find_bytype(localhost, type, id) -extern RRDSET *rrdset_find(const char *id); -extern RRDSET *rrdset_find_bytype(const char *type, const char *id); -extern RRDSET *rrdset_find_byname(const char *name); +extern RRDSET *rrdset_find_byname(RRDHOST *host, const char *name); +#define rrdset_find_byname_localhost(name) rrdset_find_byname(localhost, name) extern void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds); extern void rrdset_next_usec(RRDSET *st, usec_t microseconds); #define rrdset_next(st) rrdset_next_usec(st, 0ULL) -extern usec_t rrdset_done(RRDSET *st); +extern void rrdset_done(RRDSET *st); + +// checks if the RRDSET should be offered to viewers +#define rrdset_is_available_for_viewers(st) (rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && !rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && (st)->dimensions) // get the total duration in seconds of the round robin database #define rrdset_duration(st) ((time_t)( (((st)->counter >= ((unsigned long)(st)->entries))?(unsigned long)(st)->entries:(st)->counter) * (st)->update_every )) @@ -397,11 +604,9 @@ extern usec_t rrdset_done(RRDSET *st); // ---------------------------------------------------------------------------- // RRD DIMENSION functions -extern RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier, long divisor, int algorithm); +extern RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm); extern void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name); -extern void rrddim_free(RRDSET *st, RRDDIM *rd); - extern RRDDIM *rrddim_find(RRDSET *st, const char *id); extern int rrddim_hide(RRDSET *st, const char *id); @@ -410,4 +615,41 @@ extern int rrddim_unhide(RRDSET *st, const char *id); extern collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value); extern collected_number rrddim_set(RRDSET *st, const char *id, collected_number value); +extern long align_entries_to_pagesize(RRD_MEMORY_MODE mode, long entries); + + +// ---------------------------------------------------------------------------- +// RRD internal functions + +#ifdef NETDATA_RRD_INTERNALS + +extern avl_tree_lock rrdhost_root_index; + +extern char *rrdset_strncpyz_name(char *to, const char *from, size_t length); +extern char *rrdset_cache_dir(RRDHOST *host, const char *id, const char *config_section); + +extern void rrddim_free(RRDSET *st, RRDDIM *rd); + +extern int rrddim_compare(void* a, void* b); +extern int rrdset_compare(void* a, void* b); +extern int rrdset_compare_name(void* a, void* b); +extern int rrdfamily_compare(void *a, void *b); + +extern RRDFAMILY *rrdfamily_create(RRDHOST *host, const char *id); +extern void rrdfamily_free(RRDHOST *host, RRDFAMILY *rc); + +#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)) +extern RRDSET *rrdset_index_del_name(RRDHOST *host, RRDSET *st); + +extern void rrdset_free(RRDSET *st); +extern void rrdset_reset(RRDSET *st); +extern void rrdset_save(RRDSET *st); +extern void rrdset_delete(RRDSET *st); + +extern void rrdhost_cleanup_obsolete(RRDHOST *host); + +#endif /* NETDATA_RRD_INTERNALS */ + + #endif /* NETDATA_RRD_H */ diff --git a/src/rrd2json.c b/src/rrd2json.c index 067475006..4d853930c 100644 --- a/src/rrd2json.c +++ b/src/rrd2json.c @@ -2,7 +2,7 @@ void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used) { - pthread_rwlock_rdlock(&st->rwlock); + rrdset_rdlock(st); buffer_sprintf(wb, "\t\t{\n" @@ -29,7 +29,7 @@ void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions , st->context , st->title , st->priority - , st->enabled?"true":"false" + , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?"true":"false" , st->units , st->name , rrdset_type_name(st->chart_type) @@ -43,8 +43,8 @@ void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions size_t dimensions = 0; RRDDIM *rd; - for(rd = st->dimensions; rd ; rd = rd->next) { - if(rd->flags & RRDDIM_FLAG_HIDDEN) continue; + rrddim_foreach_read(rd, st) { + if(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) continue; memory += rd->memsize; @@ -71,58 +71,99 @@ void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions "\n\t\t}" ); - pthread_rwlock_unlock(&st->rwlock); + rrdset_unlock(st); } void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) { rrd_stats_api_v1_chart_with_data(st, wb, NULL, NULL); } -void rrd_stats_api_v1_charts(BUFFER *wb) -{ +void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb) { + static char *custom_dashboard_info_js_filename = NULL; size_t c, dimensions = 0, memory = 0, alarms = 0; RRDSET *st; + time_t now = now_realtime_sec(); + + if(unlikely(!custom_dashboard_info_js_filename)) + custom_dashboard_info_js_filename = config_get(CONFIG_SECTION_WEB, "custom dashboard_info.js", ""); + buffer_sprintf(wb, "{\n" "\t\"hostname\": \"%s\"" + ",\n\t\"version\": \"%s\"" + ",\n\t\"os\": \"%s\"" ",\n\t\"update_every\": %d" - ",\n\t\"history\": %d" + ",\n\t\"history\": %ld" + ",\n\t\"custom_info\": \"%s\"" ",\n\t\"charts\": {" - , localhost.hostname - , rrd_update_every - , rrd_default_history_entries + , host->hostname + , program_version + , host->os + , host->rrd_update_every + , host->rrd_history_entries + , custom_dashboard_info_js_filename ); - pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock); - for(st = localhost.rrdset_root, c = 0; st ; st = st->next) { - if(st->enabled && st->dimensions) { + c = 0; + rrdhost_rdlock(host); + rrdset_foreach_read(st, host) { + if(rrdset_is_available_for_viewers(st)) { if(c) buffer_strcat(wb, ","); buffer_strcat(wb, "\n\t\t\""); buffer_strcat(wb, st->id); buffer_strcat(wb, "\": "); rrd_stats_api_v1_chart_with_data(st, wb, &dimensions, &memory); + c++; + st->last_accessed_time = now; } } RRDCALC *rc; - for(rc = localhost.alarms; rc ; rc = rc->next) { + for(rc = host->alarms; rc ; rc = rc->next) { if(rc->rrdset) alarms++; } - pthread_rwlock_unlock(&localhost.rrdset_root_rwlock); + rrdhost_unlock(host); buffer_sprintf(wb, "\n\t}" ",\n\t\"charts_count\": %zu" ",\n\t\"dimensions_count\": %zu" ",\n\t\"alarms_count\": %zu" ",\n\t\"rrd_memory_bytes\": %zu" - "\n}\n" + ",\n\t\"hosts_count\": %zu" + ",\n\t\"hosts\": [" , c , dimensions , alarms , memory + , rrd_hosts_available ); + + if(unlikely(rrd_hosts_available > 1)) { + rrd_rdlock(); + RRDHOST *h; + rrdhost_foreach_read(h) { + buffer_sprintf(wb, + "%s\n\t\t{" + "\n\t\t\t\"hostname\": \"%s\"" + "\n\t\t}" + , (h != localhost) ? "," : "" + , h->hostname + ); + } + rrd_unlock(); + } + else { + buffer_sprintf(wb, + "\n\t\t{" + "\n\t\t\t\"hostname\": \"%s\"" + "\n\t\t}" + , host->hostname + ); + } + + buffer_sprintf(wb, "\n\t]\n}\n"); } // ---------------------------------------------------------------------------- @@ -145,35 +186,34 @@ static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable) #define PROMETHEUS_ELEMENT_MAX 256 -void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb) -{ - pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock); +void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb) { + rrdhost_rdlock(host); - char host[PROMETHEUS_ELEMENT_MAX + 1]; - prometheus_name_copy(host, config_get("global", "hostname", "localhost"), PROMETHEUS_ELEMENT_MAX); + char hostname[PROMETHEUS_ELEMENT_MAX + 1]; + prometheus_name_copy(hostname, host->hostname, PROMETHEUS_ELEMENT_MAX); // for each chart RRDSET *st; - for(st = localhost.rrdset_root; st ; st = st->next) { + rrdset_foreach_read(st, host) { char chart[PROMETHEUS_ELEMENT_MAX + 1]; prometheus_name_copy(chart, st->id, PROMETHEUS_ELEMENT_MAX); buffer_strcat(wb, "\n"); - if(st->enabled && st->dimensions) { - pthread_rwlock_rdlock(&st->rwlock); + if(rrdset_is_available_for_viewers(st)) { + rrdset_rdlock(st); // for each dimension RRDDIM *rd; - for(rd = st->dimensions; rd ; rd = rd->next) { - if(rd->counter) { + rrddim_foreach_read(rd, st) { + if(rd->collections_counter) { char dimension[PROMETHEUS_ELEMENT_MAX + 1]; prometheus_name_copy(dimension, rd->id, PROMETHEUS_ELEMENT_MAX); // buffer_sprintf(wb, "# HELP %s.%s %s\n", st->id, rd->id, st->units); switch(rd->algorithm) { - case RRDDIM_INCREMENTAL: - case RRDDIM_PCENT_OVER_DIFF_TOTAL: + case RRD_ALGORITHM_INCREMENTAL: + case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL: buffer_sprintf(wb, "# TYPE %s_%s counter\n", chart, dimension); break; @@ -183,21 +223,20 @@ void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb) } // calculated_number n = (calculated_number)rd->last_collected_value * (calculated_number)(abs(rd->multiplier)) / (calculated_number)(abs(rd->divisor)); - // buffer_sprintf(wb, "%s.%s " CALCULATED_NUMBER_FORMAT " %llu\n", st->id, rd->id, n, - // (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000))); + // buffer_sprintf(wb, "%s.%s " CALCULATED_NUMBER_FORMAT " %llu\n", st->id, rd->id, n, timeval_msec(&rd->last_collected_time)); buffer_sprintf(wb, "%s_%s{instance=\"%s\"} " COLLECTED_NUMBER_FORMAT " %llu\n", - chart, dimension, host, rd->last_collected_value, - (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000))); + chart, dimension, hostname, rd->last_collected_value, timeval_msec(&rd->last_collected_time) + ); } } - pthread_rwlock_unlock(&st->rwlock); + rrdset_unlock(st); } } - pthread_rwlock_unlock(&localhost.rrdset_root_rwlock); + rrdhost_unlock(host); } // ---------------------------------------------------------------------------- @@ -220,28 +259,24 @@ static inline size_t shell_name_copy(char *d, const char *s, size_t usable) { #define SHELL_ELEMENT_MAX 100 -void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb) -{ - pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock); - - char host[SHELL_ELEMENT_MAX + 1]; - shell_name_copy(host, config_get("global", "hostname", "localhost"), SHELL_ELEMENT_MAX); +void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) { + rrdhost_rdlock(host); // for each chart RRDSET *st; - for(st = localhost.rrdset_root; st ; st = st->next) { + rrdset_foreach_read(st, host) { calculated_number total = 0.0; char chart[SHELL_ELEMENT_MAX + 1]; shell_name_copy(chart, st->id, SHELL_ELEMENT_MAX); buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", st->id, st->name); - if(st->enabled && st->dimensions) { - pthread_rwlock_rdlock(&st->rwlock); + if(rrdset_is_available_for_viewers(st)) { + rrdset_rdlock(st); // for each dimension RRDDIM *rd; - for(rd = st->dimensions; rd ; rd = rd->next) { - if(rd->counter) { + rrddim_foreach_read(rd, st) { + if(rd->collections_counter) { char dimension[SHELL_ELEMENT_MAX + 1]; shell_name_copy(dimension, rd->id, SHELL_ELEMENT_MAX); @@ -252,7 +287,7 @@ void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb) else { if(rd->multiplier < 0 || rd->divisor < 0) n = -n; n = roundl(n); - if(!(rd->flags & RRDDIM_FLAG_HIDDEN)) total += n; + if(!rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) total += n; buffer_sprintf(wb, "NETDATA_%s_%s=\"%0.0Lf\" # %s\n", chart, dimension, n, st->units); } } @@ -260,14 +295,14 @@ void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb) total = roundl(total); buffer_sprintf(wb, "NETDATA_%s_VISIBLETOTAL=\"%0.0Lf\" # %s\n", chart, total, st->units); - pthread_rwlock_unlock(&st->rwlock); + rrdset_unlock(st); } } buffer_strcat(wb, "\n# NETDATA ALARMS RUNNING\n"); RRDCALC *rc; - for(rc = localhost.alarms; rc ;rc = rc->next) { + for(rc = host->alarms; rc ;rc = rc->next) { if(!rc->rrdset) continue; char chart[SHELL_ELEMENT_MAX + 1]; @@ -288,159 +323,76 @@ void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb) buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_STATUS=\"%s\"\n", chart, alarm, rrdcalc_status2string(rc->status)); } - pthread_rwlock_unlock(&localhost.rrdset_root_rwlock); + rrdhost_unlock(host); } // ---------------------------------------------------------------------------- -unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb) -{ - time_t now = now_realtime_sec(); +void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb) { + rrdhost_rdlock(host); - pthread_rwlock_rdlock(&st->rwlock); + buffer_strcat(wb, "{"); - 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; + size_t chart_counter = 0; + size_t dimension_counter = 0; - 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?",":"" + // for each chart + RRDSET *st; + rrdset_foreach_read(st, host) { + if(rrdset_is_available_for_viewers(st)) { + rrdset_rdlock(st); + + buffer_sprintf(wb, "%s\n" + "\t\"%s\": {\n" + "\t\t\"name\":\"%s\",\n" + "\t\t\"context\":\"%s\",\n" + "\t\t\"units\":\"%s\",\n" + "\t\t\"last_updated\": %ld,\n" + "\t\t\"dimensions\": {" + , chart_counter?",":"" + , st->id + , st->name + , st->context + , st->units + , rrdset_last_entry_t(st) ); - } - - buffer_sprintf(wb, - "\t\t\t],\n" - "\t\t\t\"memory\" : %lu\n" - "\t\t}" - , memory - ); - pthread_rwlock_unlock(&st->rwlock); - return memory; -} + chart_counter++; + dimension_counter = 0; -#define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n" -#define RRD_GRAPH_JSON_FOOTER "\n\t]\n}\n" + // for each dimension + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if(rd->collections_counter) { + + buffer_sprintf(wb, "%s\n" + "\t\t\t\"%s\": {\n" + "\t\t\t\t\"name\": \"%s\",\n" + "\t\t\t\t\"value\": " + , dimension_counter?",":"" + , rd->id + , rd->name + ); -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); -} + if(isnan(rd->last_stored_value)) + buffer_strcat(wb, "null"); + else + buffer_sprintf(wb, CALCULATED_NUMBER_FORMAT, rd->last_stored_value); -void rrd_stats_all_json(BUFFER *wb) -{ - unsigned long memory = 0; - long c; - RRDSET *st; + buffer_strcat(wb, "\n\t\t\t}"); - buffer_strcat(wb, RRD_GRAPH_JSON_HEADER); + dimension_counter++; + } + } - 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++; + buffer_strcat(wb, "\n\t\t}\n\t}"); + rrdset_unlock(st); } } - 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" - , localhost.hostname - , rrd_update_every - , rrd_default_history_entries - , memory - ); -} - + buffer_strcat(wb, "\n}"); + rrdhost_unlock(host); +} // ---------------------------------------------------------------------------- @@ -449,6 +401,7 @@ void rrd_stats_all_json(BUFFER *wb) #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_SELECTED 0x10 // the dimension is selected // RRDR result options #define RRDR_RESULT_OPTION_ABSOLUTE 0x00000001 @@ -541,13 +494,16 @@ static void rrdr_dump(RRDR *r) } */ -void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims) -{ +void rrdr_disable_not_selected_dimensions(RRDR *r, uint32_t options, const char *dims) { + rrdset_check_rdlock(r->st); + + if(unlikely(!dims || !*dims)) return; + char b[strlen(dims) + 1]; char *o = b, *tok; strcpy(o, dims); - long c; + long c, dims_selected = 0, dims_not_hidden_not_zero = 0; RRDDIM *d; // disable all of them @@ -561,16 +517,38 @@ void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims) // 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; + if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || (hash == d->hash_name && !strcmp(d->name, tok)))) { + + if(likely(r->od[c] & RRDR_HIDDEN)) { + r->od[c] |= RRDR_SELECTED; + r->od[c] &= ~RRDR_HIDDEN; + dims_selected++; + } // 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; + // unless option non_zero is set + if(likely(!(options & RRDR_OPTION_NONZERO))) + r->od[c] |= RRDR_NONZERO; + + // count the visible dimensions + if(likely(r->od[c] & RRDR_NONZERO)) + dims_not_hidden_not_zero++; } } } + + // check if all dimensions are hidden + if(unlikely(!dims_not_hidden_not_zero && dims_selected)) { + // there are a few selected dimensions + // but they are all zero + // enable the selected ones + // to avoid returning an empty chart + for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) + if(unlikely(r->od[c] & RRDR_SELECTED)) + r->od[c] |= RRDR_NONZERO; + } } void rrdr_buffer_print_format(BUFFER *wb, uint32_t format) @@ -624,6 +602,8 @@ void rrdr_buffer_print_format(BUFFER *wb, uint32_t format) uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims) { + rrdset_check_rdlock(r->st); + (void)dims; if(options & RRDR_OPTION_NONZERO) { @@ -659,6 +639,8 @@ uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims) void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value) { + rrdset_check_rdlock(r->st); + long rows = rrdr_rows(r); long c, i; RRDDIM *rd; @@ -840,6 +822,8 @@ 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) { + rrdset_check_rdlock(r->st); + //info("RRD2JSON(): %s: BEGIN", r->st->id); int row_annotations = 0, dates, dates_with_new = 0; char kq[2] = "", // key quote @@ -893,14 +877,14 @@ static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable) else { kq[0] = '"'; sq[0] = '"'; - if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) { - dates = JSON_DATES_TIMESTAMP; - dates_with_new = 0; - } - else { + if(options & RRDR_OPTION_GOOGLE_JSON) { dates = JSON_DATES_JS; dates_with_new = 1; } + else { + dates = JSON_DATES_TIMESTAMP; + dates_with_new = 0; + } if( options & RRDR_OPTION_OBJECTSROWS ) strcpy(pre_date, " { "); else @@ -1066,6 +1050,8 @@ static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable) static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startline, const char *separator, const char *endline, const char *betweenlines) { + rrdset_check_rdlock(r->st); + //info("RRD2CSV(): %s: BEGIN", r->st->id); long c, i; RRDDIM *d; @@ -1171,6 +1157,8 @@ static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startlin } inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, int *all_values_are_null) { + rrdset_check_rdlock(r->st); + long c; RRDDIM *d; @@ -1321,7 +1309,7 @@ inline static void rrdr_lock_rrdset(RRDR *r) { return; } - pthread_rwlock_rdlock(&r->st->rwlock); + rrdset_rdlock(r->st); r->has_st_lock = 1; } @@ -1332,7 +1320,7 @@ inline static void rrdr_unlock_rrdset(RRDR *r) { } if(likely(r->has_st_lock)) { - pthread_rwlock_unlock(&r->st->rwlock); + rrdset_unlock(r->st); r->has_st_lock = 0; } } @@ -1371,7 +1359,7 @@ static RRDR *rrdr_create(RRDSET *st, long n) rrdr_lock_rrdset(r); RRDDIM *rd; - for(rd = st->dimensions ; rd ; rd = rd->next) r->d++; + rrddim_foreach_read(rd, st) r->d++; r->n = n; @@ -1383,8 +1371,10 @@ static RRDR *rrdr_create(RRDSET *st, long n) // 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; + if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN))) + r->od[c] = RRDR_HIDDEN; + else + r->od[c] = 0; } r->c = -1; @@ -1396,7 +1386,7 @@ static RRDR *rrdr_create(RRDSET *st, long n) RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method, int aligned) { - int debug = st->debug; + int debug = rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?1:0; int absolute_period_requested = -1; time_t first_entry_t = rrdset_first_entry_t(st); @@ -1572,6 +1562,7 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g // initialize them RRDDIM *rd; long c; + rrdset_check_rdlock(st); 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; @@ -1600,7 +1591,7 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g ); r->group = group; - r->update_every = group * st->update_every; + r->update_every = (int)group * st->update_every; r->before = now; r->after = now; @@ -1752,8 +1743,20 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g return r; } -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) -{ +int rrdset2value_api_v1( + 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; @@ -1778,7 +1781,7 @@ int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensio options = rrdr_check_options(r, options, dimensions); if(dimensions) - rrdr_disable_not_selected_dimensions(r, dimensions); + rrdr_disable_not_selected_dimensions(r, options, dimensions); if(db_after) *db_after = r->after; if(db_before) *db_before = r->before; @@ -1790,8 +1793,20 @@ int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensio 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) -{ +int rrdset2anything_api_v1( + 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 +) { + st->last_accessed_time = now_realtime_sec(); + 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."); @@ -1806,7 +1821,7 @@ int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL); if(dimensions) - rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions)); + rrdr_disable_not_selected_dimensions(r, options, buffer_tostring(dimensions)); if(latest_timestamp && rrdr_rows(r) > 0) *latest_timestamp = r->before; @@ -1963,325 +1978,3 @@ int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long 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); - - - // ------------------------------------------------------------------------- - // 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; - - case DATASOURCE_JSON: - default: - break; - } - - - // ------------------------------------------------------------------------- - // validate the parameters - - 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); - - // --- - - // 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 - - 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 - - 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 - - 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 (%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 (%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; - } - - - // 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 7b1401970..f2f03c640 100644 --- a/src/rrd2json.h +++ b/src/rrd2json.h @@ -33,9 +33,11 @@ #define ALLMETRICS_FORMAT_SHELL "shell" #define ALLMETRICS_FORMAT_PROMETHEUS "prometheus" +#define ALLMETRICS_FORMAT_JSON "json" #define ALLMETRICS_SHELL 1 #define ALLMETRICS_PROMETHEUS 2 +#define ALLMETRICS_JSON 3 #define GROUP_UNDEFINED 0 #define GROUP_AVERAGE 1 @@ -59,20 +61,17 @@ #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); - -extern void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb); -extern void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb); - -extern unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb); - -extern void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb); - -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); +extern void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb); + +extern void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb); +extern void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb); +extern void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb); + +extern int rrdset2anything_api_v1(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 rrdset2value_api_v1(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/rrd2json_api_old.c b/src/rrd2json_api_old.c new file mode 100644 index 000000000..6710f31cf --- /dev/null +++ b/src/rrd2json_api_old.c @@ -0,0 +1,487 @@ +#include "common.h" + +unsigned long rrdset_info2json_api_old(RRDSET *st, char *options, BUFFER *wb) { + time_t now = now_realtime_sec(); + + rrdset_rdlock(st); + + st->last_accessed_time = now; + + 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 + , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?1:0 + , 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 + , rrdset_flag_check(st, RRDSET_FLAG_DETAIL)?1:0 + , st->usec_since_last_update + , st->collected_total + , st->last_collected_total + ); + + unsigned long memory = st->memsize; + + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + + 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\": " COLLECTED_NUMBER_FORMAT ",\n" + "\t\t\t\t\t\"divisor\": " COLLECTED_NUMBER_FORMAT ",\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 + , rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?1:0 + , rrd_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 + ); + + rrdset_unlock(st); + return memory; +} + +#define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n" +#define RRD_GRAPH_JSON_FOOTER "\n\t]\n}\n" + +void rrd_graph2json_api_old(RRDSET *st, char *options, BUFFER *wb) +{ + buffer_strcat(wb, RRD_GRAPH_JSON_HEADER); + rrdset_info2json_api_old(st, options, wb); + buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER); +} + +void rrd_all2json_api_old(RRDHOST *host, BUFFER *wb) +{ + unsigned long memory = 0; + long c = 0; + RRDSET *st; + + time_t now = now_realtime_sec(); + + buffer_strcat(wb, RRD_GRAPH_JSON_HEADER); + + rrdhost_rdlock(host); + rrdset_foreach_read(st, host) { + if(rrdset_is_available_for_viewers(st)) { + if(c) buffer_strcat(wb, ",\n"); + memory += rrdset_info2json_api_old(st, NULL, wb); + + c++; + st->last_accessed_time = now; + } + } + rrdhost_unlock(host); + + buffer_sprintf(wb, "\n\t],\n" + "\t\"hostname\": \"%s\",\n" + "\t\"update_every\": %d,\n" + "\t\"history\": %ld,\n" + "\t\"memory\": %lu\n" + "}\n" + , host->hostname + , host->rrd_update_every + , host->rrd_history_entries + , memory + ); +} + +time_t rrdset2json_api_old( + 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; + rrdset_rdlock(st); + + st->last_accessed_time = now_realtime_sec(); + + // ------------------------------------------------------------------------- + // 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; + + case DATASOURCE_JSON: + default: + break; + } + + + // ------------------------------------------------------------------------- + // validate the parameters + + 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); + + // --- + + // 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 + + int dimensions = 0; + RRDDIM *rd; + rrddim_foreach_read(rd, st) dimensions++; + if(!dimensions) { + rrdset_unlock(st); + buffer_strcat(wb, "No dimensions yet."); + return 0; + } + + + // ------------------------------------------------------------------------- + // 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, "}"); + + + // ------------------------------------------------------------------------- + // checks for debugging + + if(rrdset_flag_check(st, RRDSET_FLAG_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 (%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 (%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] = rrddim_flag_check(rd, 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(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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 %zu bytes", st->name, wb->len); + + rrdset_unlock(st); + return last_timestamp; +} diff --git a/src/rrd2json_api_old.h b/src/rrd2json_api_old.h new file mode 100644 index 000000000..f8c63814f --- /dev/null +++ b/src/rrd2json_api_old.h @@ -0,0 +1,14 @@ +#ifndef NETDATA_RRD2JSON_API_OLD_H +#define NETDATA_RRD2JSON_API_OLD_H + +extern unsigned long rrdset_info2json_api_old(RRDSET *st, char *options, BUFFER *wb); + +extern void rrd_graph2json_api_old(RRDSET *st, char *options, BUFFER *wb); + +extern void rrd_all2json_api_old(RRDHOST *host, BUFFER *wb); + +extern time_t rrdset2json_api_old(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); + + +#endif //NETDATA_RRD2JSON_API_OLD_H diff --git a/src/rrdcalc.c b/src/rrdcalc.c new file mode 100644 index 000000000..1f1845409 --- /dev/null +++ b/src/rrdcalc.c @@ -0,0 +1,415 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDCALC management + +inline const char *rrdcalc_status2string(int status) { + switch(status) { + case RRDCALC_STATUS_REMOVED: + return "REMOVED"; + + case RRDCALC_STATUS_UNDEFINED: + return "UNDEFINED"; + + case RRDCALC_STATUS_UNINITIALIZED: + return "UNINITIALIZED"; + + 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: + error("Unknown alarm status %d", status); + 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 = now_realtime_sec(); + rc->rrdset = st; + + rc->rrdset_next = st->alarms; + rc->rrdset_prev = NULL; + + if(rc->rrdset_next) + rc->rrdset_next->rrdset_prev = rc; + + 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); + + { + time_t now = now_realtime_sec(); + health_alarm_log( + st->rrdhost, + rc->id, + rc->next_event_id++, + now, + rc->name, + rc->rrdset->id, + rc->rrdset->family, + rc->exec, + rc->recipient, + now - rc->last_status_change, + rc->old_value, + rc->value, + rc->status, + RRDCALC_STATUS_UNINITIALIZED, + rc->source, + rc->units, + rc->info, + 0, + 0 + ); + } +} + +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(unlikely(rc->rrdset)) + continue; + + if(unlikely(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) { + debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); + error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name); + return; + } + + { + time_t now = now_realtime_sec(); + health_alarm_log( + st->rrdhost, + rc->id, + rc->next_event_id++, + now, + rc->name, + rc->rrdset->id, + rc->rrdset->family, + rc->exec, + rc->recipient, + now - rc->last_status_change, + rc->old_value, + rc->value, + rc->status, + RRDCALC_STATUS_REMOVED, + rc->source, + rc->units, + rc->info, + 0, + 0 + ); + } + + 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(unlikely(rc->hash == hash && !strcmp(rc->name, name))) + return rc; + } + + return NULL; +} + +inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name) { + RRDCALC *rc; + + if(unlikely(!chart)) { + error("attempt to find RRDCALC '%s' without giving a chart name", name); + return 1; + } + + if(unlikely(!hash_chart)) hash_chart = simple_hash(chart); + if(unlikely(!hash_name)) hash_name = simple_hash(name); + + // make sure it does not already exist + for(rc = host->alarms; rc ; rc = rc->next) { + if (unlikely(rc->chart && rc->hash == hash_name && rc->hash_chart == hash_chart && !strcmp(name, rc->name) && !strcmp(chart, rc->chart))) { + debug(D_HEALTH, "Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); + error("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); + return 1; + } + } + + return 0; +} + +inline uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id) { + if(chart && name) { + uint32_t hash_chart = simple_hash(chart); + uint32_t hash_name = simple_hash(name); + + // re-use old IDs, by looking them up in the alarm log + ALARM_ENTRY *ae; + for(ae = host->health_log.alarms; ae ;ae = ae->next) { + if(unlikely(ae->hash_name == hash_name && ae->hash_chart == hash_chart && !strcmp(name, ae->name) && !strcmp(chart, ae->chart))) { + if(next_event_id) *next_event_id = ae->alarm_event_id + 1; + return ae->alarm_id; + } + } + } + + return host->health_log.next_alarm_id++; +} + +inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) { + rrdhost_check_rdlock(host); + + if(rc->calculation) { + rc->calculation->status = &rc->status; + 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->status = &rc->status; + 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->status = &rc->status; + 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 + if(likely(host->alarms)) { + // append it + RRDCALC *t; + for(t = host->alarms; t && t->next ; t = t->next) ; + t->next = rc; + } + else { + host->alarms = rc; + } + + // link it to its chart + RRDSET *st; + rrdset_foreach_read(st, host) { + if(rrdcalc_is_matching_this_rrdset(rc, st)) { + rrdsetcalc_link(st, rc); + break; + } + } +} + +inline RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart) { + + debug(D_HEALTH, "Health creating dynamic alarm (from template) '%s.%s'", chart, rt->name); + + if(rrdcalc_exists(host, chart, rt->name, 0, 0)) + return NULL; + + RRDCALC *rc = callocz(1, sizeof(RRDCALC)); + rc->next_event_id = 1; + rc->id = rrdcalc_get_unique_id(host, chart, rt->name, &rc->next_event_id); + rc->name = strdupz(rt->name); + rc->hash = simple_hash(rc->name); + rc->chart = strdupz(chart); + rc->hash_chart = simple_hash(rc->chart); + + if(rt->dimensions) rc->dimensions = strdupz(rt->dimensions); + + rc->green = rt->green; + rc->red = rt->red; + rc->value = NAN; + rc->old_value = NAN; + + rc->delay_up_duration = rt->delay_up_duration; + rc->delay_down_duration = rt->delay_down_duration; + rc->delay_max_duration = rt->delay_max_duration; + rc->delay_multiplier = rt->delay_multiplier; + + rc->group = rt->group; + rc->after = rt->after; + rc->before = rt->before; + rc->update_every = rt->update_every; + rc->options = rt->options; + + if(rt->exec) rc->exec = strdupz(rt->exec); + if(rt->recipient) rc->recipient = strdupz(rt->recipient); + if(rt->source) rc->source = strdupz(rt->source); + if(rt->units) rc->units = strdupz(rt->units); + if(rt->info) rc->info = strdupz(rt->info); + + if(rt->calculation) { + rc->calculation = expression_parse(rt->calculation->source, NULL, NULL); + if(!rc->calculation) + error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, rt->name, rt->calculation->source); + } + if(rt->warning) { + rc->warning = expression_parse(rt->warning->source, NULL, NULL); + if(!rc->warning) + error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, rt->name, rt->warning->source); + } + if(rt->critical) { + rc->critical = expression_parse(rt->critical->source, NULL, NULL); + if(!rc->critical) + error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, rt->name, rt->critical->source); + } + + debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', recipient '%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', delay up %d, delay down %d, delay max %d, delay_multiplier %f", + (rc->chart)?rc->chart:"NOCHART", + rc->name, + (rc->exec)?rc->exec:"DEFAULT", + (rc->recipient)?rc->recipient:"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, + rc->delay_up_duration, + rc->delay_down_duration, + rc->delay_max_duration, + rc->delay_multiplier + ); + + 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(unlikely(rc == host->alarms)) + host->alarms = rc->next; + + else { + RRDCALC *t; + for(t = host->alarms; t && t->next != rc; t = t->next) ; + if(t) { + t->next = rc->next; + rc->next = NULL; + } + else + error("Cannot unlink alarm '%s.%s' from host '%s': not found", 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->recipient); + freez(rc->source); + freez(rc->units); + freez(rc->info); + freez(rc); +} diff --git a/src/rrdcalctemplate.c b/src/rrdcalctemplate.c new file mode 100644 index 000000000..2c5e2bd16 --- /dev/null +++ b/src/rrdcalctemplate.c @@ -0,0 +1,62 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// 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) + && (!rt->family_pattern || simple_pattern_matches(rt->family_pattern, st->family))) { + RRDCALC *rc = rrdcalc_create(st->rrdhost, rt, st->id); + if(unlikely(!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); +#endif + } + } +} + +inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) { + if(unlikely(!rt)) return; + + debug(D_HEALTH, "Health removing template '%s' of host '%s'", rt->name, host->hostname); + + if(host->templates == rt) { + host->templates = rt->next; + } + else { + RRDCALCTEMPLATE *t; + for (t = host->templates; t && t->next != rt; t = t->next ) ; + if(t) { + t->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->family_match); + simple_pattern_free(rt->family_pattern); + + freez(rt->name); + freez(rt->exec); + freez(rt->recipient); + freez(rt->context); + freez(rt->source); + freez(rt->units); + freez(rt->info); + freez(rt->dimensions); + freez(rt); +} + + diff --git a/src/rrddim.c b/src/rrddim.c new file mode 100644 index 000000000..54a17522f --- /dev/null +++ b/src/rrddim.c @@ -0,0 +1,313 @@ +#define NETDATA_RRD_INTERNALS 1 +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDDIM index + +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); +} + +#define rrddim_index_add(st, rd) (RRDDIM *)avl_insert_lock(&((st)->dimensions_index), (avl *)(rd)) +#define rrddim_index_del(st,rd ) (RRDDIM *)avl_remove_lock(&((st)->dimensions_index), (avl *)(rd)) + +static inline RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) { + RRDDIM tmp = { + .id = id, + .hash = (hash)?hash:simple_hash(id) + }; + return (RRDDIM *)avl_search_lock(&(st->dimensions_index), (avl *) &tmp); +} + + +// ---------------------------------------------------------------------------- +// RRDDIM - find a dimension + +inline RRDDIM *rrddim_find(RRDSET *st, const char *id) { + debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", st->name, id); + + return rrddim_index_find(st, id, 0); +} + + +// ---------------------------------------------------------------------------- +// RRDDIM rename a dimension + +inline void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name) { + if(unlikely(!strcmp(rd->name, name))) + return; + + debug(D_RRD_CALLS, "rrddim_set_name() from %s.%s to %s.%s", st->name, rd->name, st->name, name); + + char varname[CONFIG_MAX_NAME + 1]; + snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id); + rd->name = config_set_default(st->config_section, varname, name); + rd->hash_name = simple_hash(rd->name); + + rrddimvar_rename_all(rd); +} + + +// ---------------------------------------------------------------------------- +// RRDDIM create a dimension + +RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm) { + RRDDIM *rd = rrddim_find(st, id); + if(unlikely(rd)) { + debug(D_RRD_CALLS, "Cannot create rrd dimension '%s/%s', it already exists.", st->id, name?name:"<NONAME>"); + return rd; + } + + char filename[FILENAME_MAX + 1]; + char fullfilename[FILENAME_MAX + 1]; + + char varname[CONFIG_MAX_NAME + 1]; + 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(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { + rd = (RRDDIM *)mymmap(fullfilename, size, ((st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 1); + if(likely(rd)) { + // we have a file mapped for rd + + memset(&rd->avl, 0, sizeof(avl)); + rd->id = NULL; + rd->name = NULL; + rd->cache_filename = NULL; + rd->variables = NULL; + rd->next = NULL; + rd->rrdset = NULL; + + struct timeval now; + now_realtime_timeval(&now); + + if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) { + errno = 0; + info("Initializing file %s.", fullfilename); + memset(rd, 0, size); + } + else if(rd->memsize != size) { + errno = 0; + error("File %s does not have the desired size. Clearing it.", fullfilename); + memset(rd, 0, size); + } + else if(rd->multiplier != multiplier) { + errno = 0; + error("File %s does not have the same multiplier. Clearing it.", fullfilename); + memset(rd, 0, size); + } + else if(rd->divisor != divisor) { + errno = 0; + error("File %s does not have the same divisor. Clearing it.", fullfilename); + memset(rd, 0, size); + } + else if(rd->update_every != st->update_every) { + errno = 0; + error("File %s does not have the same refresh frequency. Clearing it.", fullfilename); + memset(rd, 0, size); + } + else if(dt_usec(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * USEC_PER_SEC)) { + errno = 0; + error("File %s is too old. Clearing it.", fullfilename); + memset(rd, 0, size); + } + + if(rd->algorithm && rd->algorithm != algorithm) + error("File %s does not have the expected algorithm (expected %u '%s', found %u '%s'). Previous values may be wrong." + , fullfilename, algorithm, rrd_algorithm_name(algorithm), rd->algorithm, + rrd_algorithm_name(rd->algorithm)); + + // make sure we have the right memory mode + // even if we cleared the memory + rd->rrd_memory_mode = st->rrd_memory_mode; + } + } + + if(unlikely(!rd)) { + // if we didn't manage to get a mmap'd dimension, just create one + rd = callocz(1, size); + rd->rrd_memory_mode = (st->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM; + } + + rd->memsize = size; + + strcpy(rd->magic, RRDDIMENSION_MAGIC); + + rd->id = strdupz(id); + rd->hash = simple_hash(rd->id); + + rd->cache_filename = strdupz(fullfilename); + + snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id); + rd->name = config_get(st->config_section, varname, (name && *name)?name:rd->id); + rd->hash_name = simple_hash(rd->name); + + snprintfz(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id); + rd->algorithm = rrd_algorithm_id(config_get(st->config_section, varname, rrd_algorithm_name(algorithm))); + + snprintfz(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id); + rd->multiplier = config_get_number(st->config_section, varname, multiplier); + + snprintfz(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id); + rd->divisor = config_get_number(st->config_section, varname, divisor); + if(!rd->divisor) rd->divisor = 1; + + rd->entries = st->entries; + rd->update_every = st->update_every; + + // prevent incremental calculation spikes + rd->collections_counter = 0; + rd->updated = 0; + rd->flags = 0x00000000; + + 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 + rrdset_wrlock(st); + if(!st->dimensions) + st->dimensions = rd; + else { + RRDDIM *td = st->dimensions; + for(; td->next; td = td->next) ; + td->next = rd; + } + + if(st->rrdhost->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); + } + + rrdset_unlock(st); + + if(unlikely(rrddim_index_add(st, rd) != rd)) + error("RRDDIM: INTERNAL ERROR: attempt to index duplicate dimension '%s' on chart '%s'", rd->id, st->id); + + return(rd); +} + + +// ---------------------------------------------------------------------------- +// RRDDIM remove / free a dimension + +void rrddim_free(RRDSET *st, RRDDIM *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); + + if(unlikely(rrddim_index_del(st, rd) != rd)) + error("RRDDIM: INTERNAL ERROR: attempt to remove from index dimension '%s' on chart '%s', removed a different dimension.", rd->id, st->id); + + // free(rd->annotations); + + switch(rd->rrd_memory_mode) { + case 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); + // continue to map mode - no break; + + case RRD_MEMORY_MODE_MAP: + debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name); + freez((void *)rd->id); + freez(rd->cache_filename); + munmap(rd, rd->memsize); + break; + + case RRD_MEMORY_MODE_NONE: + case RRD_MEMORY_MODE_RAM: + debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name); + freez((void *)rd->id); + freez(rd->cache_filename); + freez(rd); + break; + } +} + + +// ---------------------------------------------------------------------------- +// RRDDIM - set dimension options + +int rrddim_hide(RRDSET *st, const char *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_flag_set(rd, 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); + + 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_flag_clear(rd, RRDDIM_FLAG_HIDDEN); + return 0; +} + + +// ---------------------------------------------------------------------------- +// RRDDIM - collect values for a dimension + +inline 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); + + now_realtime_timeval(&rd->last_collected_time); + rd->collected_value = value; + rd->updated = 1; + + rd->collections_counter++; + + // fprintf(stderr, "%s.%s %llu " COLLECTED_NUMBER_FORMAT " dt %0.6f" " rate " CALCULATED_NUMBER_FORMAT "\n", st->name, rd->name, st->usec_since_last_update, value, (float)((double)st->usec_since_last_update / (double)1000000), (calculated_number)((value - rd->last_collected_value) * (calculated_number)rd->multiplier / (calculated_number)rd->divisor * 1000000.0 / (calculated_number)st->usec_since_last_update)); + + 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; + } + + return rrddim_set_by_pointer(st, rd, value); +} diff --git a/src/rrddimvar.c b/src/rrddimvar.c new file mode 100644 index 000000000..f6eb6d8ef --- /dev/null +++ b/src/rrddimvar.c @@ -0,0 +1,210 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDDIMVAR management +// DIMENSION VARIABLES + +#define RRDDIMVAR_ID_MAX 1024 + +static inline void rrddimvar_free_variables(RRDDIMVAR *rs) { + RRDDIM *rd = rs->rrddim; + RRDSET *st = rd->rrdset; + + // CHART VARIABLES FOR THIS DIMENSION + + rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_id); + rs->var_local_id = NULL; + + rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local_name); + rs->var_local_name = NULL; + + // FAMILY VARIABLES FOR THIS DIMENSION + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_id); + rs->var_family_id = NULL; + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); + rs->var_family_name = NULL; + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextid); + rs->var_family_contextid = NULL; + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_contextname); + rs->var_family_contextname = NULL; + + // HOST VARIABLES FOR THIS DIMENSION + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidid); + rs->var_host_chartidid = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartidname); + rs->var_host_chartidname = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnameid); + rs->var_host_chartnameid = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_chartnamename); + rs->var_host_chartnamename = NULL; + + // KEYS + + freez(rs->key_id); + rs->key_id = NULL; + + freez(rs->key_name); + rs->key_name = NULL; + + freez(rs->key_fullidid); + rs->key_fullidid = NULL; + + freez(rs->key_fullidname); + rs->key_fullidname = NULL; + + freez(rs->key_contextid); + rs->key_contextid = NULL; + + freez(rs->key_contextname); + rs->key_contextname = NULL; + + freez(rs->key_fullnameid); + rs->key_fullnameid = NULL; + + freez(rs->key_fullnamename); + rs->key_fullnamename = NULL; +} + +static inline void rrddimvar_create_variables(RRDDIMVAR *rs) { + rrddimvar_free_variables(rs); + + RRDDIM *rd = rs->rrddim; + RRDSET *st = rd->rrdset; + + char buffer[RRDDIMVAR_ID_MAX + 1]; + + // KEYS + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix); + rs->key_id = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix); + rs->key_name = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_id); + rs->key_fullidid = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->key_name); + rs->key_fullidname = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_id); + rs->key_contextid = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->context, rs->key_name); + rs->key_contextname = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_id); + rs->key_fullnameid = strdupz(buffer); + + snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->key_name); + rs->key_fullnamename = strdupz(buffer); + + // CHART VARIABLES FOR THIS DIMENSION + // ----------------------------------- + // + // dimensions are available as: + // - $id + // - $name + + rs->var_local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_id, rs->type, rs->value); + rs->var_local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->key_name, rs->type, rs->value); + + // FAMILY VARIABLES FOR THIS DIMENSION + // ----------------------------------- + // + // dimensions are available as: + // - $id (only the first, when multiple overlap) + // - $name (only the first, when multiple overlap) + // - $chart-context.id + // - $chart-context.name + + rs->var_family_id = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_id, rs->type, rs->value); + rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_name, rs->type, rs->value); + rs->var_family_contextid = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextid, rs->type, rs->value); + rs->var_family_contextname = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_contextname, rs->type, rs->value); + + // HOST VARIABLES FOR THIS DIMENSION + // ----------------------------------- + // + // dimensions are available as: + // - $chart-id.id + // - $chart-id.name + // - $chart-name.id + // - $chart-name.name + + rs->var_host_chartidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidid, rs->type, rs->value); + rs->var_host_chartidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullidname, rs->type, rs->value); + rs->var_host_chartnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnameid, rs->type, rs->value); + rs->var_host_chartnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullnamename, rs->type, rs->value); +} + +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 = ""; + + RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR)); + + rs->prefix = strdupz(prefix); + rs->suffix = strdupz(suffix); + + rs->type = type; + rs->value = value; + rs->options = options; + rs->rrddim = rd; + + rs->next = rd->variables; + rd->variables = rs; + + rrddimvar_create_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; + rrddimvar_create_variables(rs); + } +} + +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); + + rrddimvar_free_variables(rs); + + 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->key_name, st->id, rd->id); + else t->next = rs->next; + } + + freez(rs->prefix); + freez(rs->suffix); + freez(rs); +} + diff --git a/src/rrdfamily.c b/src/rrdfamily.c new file mode 100644 index 000000000..fc203e915 --- /dev/null +++ b/src/rrdfamily.c @@ -0,0 +1,58 @@ +#define NETDATA_RRD_INTERNALS 1 +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDFAMILY index + +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(RRDHOST *host, const char *id) { + RRDFAMILY *rc = rrdfamily_index_find(host, 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(host, rc); + if(ret != rc) + fatal("RRDFAMILY: 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(RRDHOST *host, RRDFAMILY *rc) { + rc->use_count--; + if(!rc->use_count) { + RRDFAMILY *ret = rrdfamily_index_del(host, rc); + if(ret != rc) + fatal("RRDFAMILY: 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("RRDFAMILY: INTERNAL ERROR: Variables index of RRDFAMILY '%s' that is freed, is not empty.", rc->family); + + freez((void *)rc->family); + freez(rc); + } +} + diff --git a/src/rrdhost.c b/src/rrdhost.c new file mode 100644 index 000000000..a2310330d --- /dev/null +++ b/src/rrdhost.c @@ -0,0 +1,583 @@ +#define NETDATA_RRD_INTERNALS 1 +#include "common.h" + +RRDHOST *localhost = NULL; +size_t rrd_hosts_available = 0; +netdata_rwlock_t rrd_rwlock = NETDATA_RWLOCK_INITIALIZER; + +time_t rrdset_free_obsolete_time = 3600; +time_t rrdhost_free_orphan_time = 3600; + +// ---------------------------------------------------------------------------- +// RRDHOST index + +int rrdhost_compare(void* a, void* b) { + if(((RRDHOST *)a)->hash_machine_guid < ((RRDHOST *)b)->hash_machine_guid) return -1; + else if(((RRDHOST *)a)->hash_machine_guid > ((RRDHOST *)b)->hash_machine_guid) return 1; + else return strcmp(((RRDHOST *)a)->machine_guid, ((RRDHOST *)b)->machine_guid); +} + +avl_tree_lock rrdhost_root_index = { + .avl_tree = { NULL, rrdhost_compare }, + .rwlock = AVL_LOCK_INITIALIZER +}; + +RRDHOST *rrdhost_find_by_guid(const char *guid, uint32_t hash) { + debug(D_RRDHOST, "Searching in index for host with guid '%s'", guid); + + RRDHOST tmp; + strncpyz(tmp.machine_guid, guid, GUID_LEN); + tmp.hash_machine_guid = (hash)?hash:simple_hash(tmp.machine_guid); + + return (RRDHOST *)avl_search_lock(&(rrdhost_root_index), (avl *) &tmp); +} + +RRDHOST *rrdhost_find_by_hostname(const char *hostname, uint32_t hash) { + if(unlikely(!strcmp(hostname, "localhost"))) + return localhost; + + if(unlikely(!hash)) hash = simple_hash(hostname); + + rrd_rdlock(); + RRDHOST *host; + rrdhost_foreach_read(host) { + if(unlikely((hash == host->hash_hostname && !strcmp(hostname, host->hostname)))) { + rrd_unlock(); + return host; + } + } + rrd_unlock(); + + return NULL; +} + +#define rrdhost_index_add(rrdhost) (RRDHOST *)avl_insert_lock(&(rrdhost_root_index), (avl *)(rrdhost)) +#define rrdhost_index_del(rrdhost) (RRDHOST *)avl_remove_lock(&(rrdhost_root_index), (avl *)(rrdhost)) + + +// ---------------------------------------------------------------------------- +// RRDHOST - internal helpers + +static inline void rrdhost_init_hostname(RRDHOST *host, const char *hostname) { + freez(host->hostname); + host->hostname = strdupz(hostname); + host->hash_hostname = simple_hash(host->hostname); +} + +static inline void rrdhost_init_os(RRDHOST *host, const char *os) { + freez(host->os); + host->os = strdupz(os?os:"unknown"); +} + +static inline void rrdhost_init_machine_guid(RRDHOST *host, const char *machine_guid) { + strncpy(host->machine_guid, machine_guid, GUID_LEN); + host->machine_guid[GUID_LEN] = '\0'; + host->hash_machine_guid = simple_hash(host->machine_guid); +} + + +// ---------------------------------------------------------------------------- +// RRDHOST - add a host + +RRDHOST *rrdhost_create(const char *hostname, + const char *guid, + const char *os, + int update_every, + long entries, + RRD_MEMORY_MODE memory_mode, + int health_enabled, + int rrdpush_enabled, + char *rrdpush_destination, + char *rrdpush_api_key, + int is_localhost +) { + debug(D_RRDHOST, "Host '%s': adding with guid '%s'", hostname, guid); + + rrd_check_wrlock(); + + RRDHOST *host = callocz(1, sizeof(RRDHOST)); + + host->rrd_update_every = (update_every > 0)?update_every:1; + host->rrd_history_entries = align_entries_to_pagesize(memory_mode, entries); + host->rrd_memory_mode = memory_mode; + host->health_enabled = (memory_mode == RRD_MEMORY_MODE_NONE)? 0 : health_enabled; + host->rrdpush_enabled = (rrdpush_enabled && rrdpush_destination && *rrdpush_destination && rrdpush_api_key && *rrdpush_api_key); + host->rrdpush_destination = (host->rrdpush_enabled)?strdupz(rrdpush_destination):NULL; + host->rrdpush_api_key = (host->rrdpush_enabled)?strdupz(rrdpush_api_key):NULL; + + host->rrdpush_pipe[0] = -1; + host->rrdpush_pipe[1] = -1; + host->rrdpush_socket = -1; + + netdata_mutex_init(&host->rrdpush_mutex); + netdata_rwlock_init(&host->rrdhost_rwlock); + + rrdhost_init_hostname(host, hostname); + rrdhost_init_machine_guid(host, guid); + rrdhost_init_os(host, os); + + avl_init_lock(&(host->rrdset_root_index), rrdset_compare); + avl_init_lock(&(host->rrdset_root_index_name), rrdset_compare_name); + avl_init_lock(&(host->rrdfamily_root_index), rrdfamily_compare); + avl_init_lock(&(host->variables_root_index), rrdvar_compare); + + if(config_get_boolean(CONFIG_SECTION_GLOBAL, "delete obsolete charts files", 1)) + rrdhost_flag_set(host, RRDHOST_DELETE_OBSOLETE_FILES); + + if(config_get_boolean(CONFIG_SECTION_GLOBAL, "delete orphan hosts files", 1) && !is_localhost) + rrdhost_flag_set(host, RRDHOST_DELETE_ORPHAN_FILES); + + + // ------------------------------------------------------------------------ + // initialize health variables + + host->health_log.next_log_id = 1; + host->health_log.next_alarm_id = 1; + host->health_log.max = 1000; + host->health_log.next_log_id = + host->health_log.next_alarm_id = (uint32_t)now_realtime_sec(); + + long n = config_get_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", host->health_log.max); + if(n < 10) { + error("Host '%s': health configuration has invalid max log entries %ld. Using default %u", host->hostname, n, host->health_log.max); + config_set_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", (long)host->health_log.max); + } + else + host->health_log.max = (unsigned int)n; + + netdata_rwlock_init(&host->health_log.alarm_log_rwlock); + + char filename[FILENAME_MAX + 1]; + + if(is_localhost) { + + host->cache_dir = strdupz(netdata_configured_cache_dir); + host->varlib_dir = strdupz(netdata_configured_varlib_dir); + + } + else { + // this is not localhost - append our GUID to localhost path + + snprintfz(filename, FILENAME_MAX, "%s/%s", netdata_configured_cache_dir, host->machine_guid); + host->cache_dir = strdupz(filename); + + if(host->rrd_memory_mode == RRD_MEMORY_MODE_MAP || host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) { + int r = mkdir(host->cache_dir, 0775); + if(r != 0 && errno != EEXIST) + error("Host '%s': cannot create directory '%s'", host->hostname, host->cache_dir); + } + + snprintfz(filename, FILENAME_MAX, "%s/%s", netdata_configured_varlib_dir, host->machine_guid); + host->varlib_dir = strdupz(filename); + + if(host->health_enabled) { + int r = mkdir(host->varlib_dir, 0775); + if(r != 0 && errno != EEXIST) + error("Host '%s': cannot create directory '%s'", host->hostname, host->varlib_dir); + } + + } + + if(host->health_enabled) { + snprintfz(filename, FILENAME_MAX, "%s/health", host->varlib_dir); + int r = mkdir(filename, 0775); + if(r != 0 && errno != EEXIST) + error("Host '%s': cannot create directory '%s'", host->hostname, filename); + } + + snprintfz(filename, FILENAME_MAX, "%s/health/health-log.db", host->varlib_dir); + host->health_log_filename = strdupz(filename); + + snprintfz(filename, FILENAME_MAX, "%s/alarm-notify.sh", netdata_configured_plugins_dir); + host->health_default_exec = strdupz(config_get(CONFIG_SECTION_HEALTH, "script to execute on alarm", filename)); + host->health_default_recipient = strdup("root"); + + + // ------------------------------------------------------------------------ + // load health configuration + + if(host->health_enabled) { + health_alarm_log_load(host); + health_alarm_log_open(host); + + rrdhost_wrlock(host); + health_readdir(host, health_config_dir()); + rrdhost_unlock(host); + } + + + // ------------------------------------------------------------------------ + // link it and add it to the index + + if(is_localhost) { + host->next = localhost; + localhost = host; + } + else { + if(localhost) { + host->next = localhost->next; + localhost->next = host; + } + else localhost = host; + } + + RRDHOST *t = rrdhost_index_add(host); + + if(t != host) { + error("Host '%s': cannot add host with machine guid '%s' to index. It already exists as host '%s' with machine guid '%s'.", host->hostname, host->machine_guid, t->hostname, t->machine_guid); + rrdhost_free(host); + host = NULL; + } + else { + info("Host '%s' with guid '%s' initialized" + ", os %s" + ", update every %d" + ", memory mode %s" + ", history entries %ld" + ", streaming %s" + " (to '%s' with api key '%s')" + ", health %s" + ", cache_dir '%s'" + ", varlib_dir '%s'" + ", health_log '%s'" + ", alarms default handler '%s'" + ", alarms default recipient '%s'" + , host->hostname + , host->machine_guid + , host->os + , host->rrd_update_every + , rrd_memory_mode_name(host->rrd_memory_mode) + , host->rrd_history_entries + , host->rrdpush_enabled?"enabled":"disabled" + , host->rrdpush_destination?host->rrdpush_destination:"" + , host->rrdpush_api_key?host->rrdpush_api_key:"" + , host->health_enabled?"enabled":"disabled" + , host->cache_dir + , host->varlib_dir + , host->health_log_filename + , host->health_default_exec + , host->health_default_recipient + ); + } + + rrd_hosts_available++; + + return host; +} + +RRDHOST *rrdhost_find_or_create( + const char *hostname + , const char *guid + , const char *os + , int update_every + , long history + , RRD_MEMORY_MODE mode + , int health_enabled + , int rrdpush_enabled + , char *rrdpush_destination + , char *rrdpush_api_key +) { + debug(D_RRDHOST, "Searching for host '%s' with guid '%s'", hostname, guid); + + rrd_wrlock(); + RRDHOST *host = rrdhost_find_by_guid(guid, 0); + if(!host) { + host = rrdhost_create( + hostname + , guid + , os + , update_every + , history + , mode + , health_enabled + , rrdpush_enabled + , rrdpush_destination + , rrdpush_api_key + , 0 + ); + } + else { + host->health_enabled = health_enabled; + + if(strcmp(host->hostname, hostname)) { + char *t = host->hostname; + host->hostname = strdupz(hostname); + host->hash_hostname = simple_hash(host->hostname); + freez(t); + } + + if(host->rrd_update_every != update_every) + error("Host '%s' has an update frequency of %d seconds, but the wanted one is %d seconds.", host->hostname, host->rrd_update_every, update_every); + + if(host->rrd_history_entries != history) + error("Host '%s' has history of %ld entries, but the wanted one is %ld entries.", host->hostname, host->rrd_history_entries, history); + + if(host->rrd_memory_mode != mode) + error("Host '%s' has memory mode '%s', but the wanted one is '%s'.", host->hostname, rrd_memory_mode_name(host->rrd_memory_mode), rrd_memory_mode_name(mode)); + } + rrd_unlock(); + + rrdhost_cleanup_orphan(host); + + return host; +} + +static inline int rrdhost_should_be_deleted(RRDHOST *host, RRDHOST *protected, time_t now) { + if(host != protected + && host != localhost + && !host->connected_senders + && host->senders_disconnected_time + && host->senders_disconnected_time + rrdhost_free_orphan_time < now) + return 1; + + return 0; +} + +void rrdhost_cleanup_orphan(RRDHOST *protected) { + time_t now = now_realtime_sec(); + + rrd_wrlock(); + + RRDHOST *host; + +restart_after_removal: + rrdhost_foreach_write(host) { + if(rrdhost_should_be_deleted(host, protected, now)) { + info("Host '%s' with machine guid '%s' is obsolete - cleaning up.", host->hostname, host->machine_guid); + + if(rrdset_flag_check(host, RRDHOST_ORPHAN)) + rrdhost_delete(host); + else + rrdhost_save(host); + + rrdhost_free(host); + goto restart_after_removal; + } + } + + rrd_unlock(); +} + +// ---------------------------------------------------------------------------- +// RRDHOST global / startup initialization + +void rrd_init(char *hostname) { + rrdset_free_obsolete_time = config_get_number(CONFIG_SECTION_GLOBAL, "cleanup obsolete charts after seconds", rrdset_free_obsolete_time); + + health_init(); + registry_init(); + rrdpush_init(); + + debug(D_RRDHOST, "Initializing localhost with hostname '%s'", hostname); + rrd_wrlock(); + localhost = rrdhost_create( + hostname + , registry_get_this_machine_guid() + , os_type + , default_rrd_update_every + , default_rrd_history_entries + , default_rrd_memory_mode + , default_health_enabled + , default_rrdpush_enabled + , default_rrdpush_destination + , default_rrdpush_api_key + , 1 + ); + rrd_unlock(); +} + +// ---------------------------------------------------------------------------- +// RRDHOST - lock validations +// there are only used when NETDATA_INTERNAL_CHECKS is set + +void __rrdhost_check_rdlock(RRDHOST *host, const char *file, const char *function, const unsigned long line) { + debug(D_RRDHOST, "Checking read lock on host '%s'", host->hostname); + + int ret = netdata_rwlock_trywrlock(&host->rrdhost_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(RRDHOST *host, const char *file, const char *function, const unsigned long line) { + debug(D_RRDHOST, "Checking write lock on host '%s'", host->hostname); + + int ret = netdata_rwlock_tryrdlock(&host->rrdhost_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); +} + +void __rrd_check_rdlock(const char *file, const char *function, const unsigned long line) { + debug(D_RRDHOST, "Checking read lock on all RRDs"); + + int ret = netdata_rwlock_trywrlock(&rrd_rwlock); + if(ret == 0) + fatal("RRDs should be read-locked, but it are not, at function %s() at line %lu of file '%s'", function, line, file); +} + +void __rrd_check_wrlock(const char *file, const char *function, const unsigned long line) { + debug(D_RRDHOST, "Checking write lock on all RRDs"); + + int ret = netdata_rwlock_tryrdlock(&rrd_rwlock); + if(ret == 0) + fatal("RRDs should be write-locked, but it are not, at function %s() at line %lu of file '%s'", function, line, file); +} + +// ---------------------------------------------------------------------------- +// RRDHOST - free + +void rrdhost_free(RRDHOST *host) { + if(!host) return; + + info("Freeing all memory for host '%s'...", host->hostname); + + rrd_check_wrlock(); // make sure the RRDs are write locked + + // stop a possibly running thread + rrdpush_sender_thread_stop(host); + + rrdhost_wrlock(host); // lock this RRDHOST + + // ------------------------------------------------------------------------ + // release its children resources + + while(host->rrdset_root) rrdset_free(host->rrdset_root); + + while(host->alarms) rrdcalc_free(host, host->alarms); + while(host->templates) rrdcalctemplate_free(host, host->templates); + health_alarm_log_free(host); + + + // ------------------------------------------------------------------------ + // remove it from the indexes + + if(rrdhost_index_del(host) != host) + error("RRDHOST '%s' removed from index, deleted the wrong entry.", host->hostname); + + + // ------------------------------------------------------------------------ + // unlink it from the host + + if(host == localhost) { + localhost = host->next; + } + else { + // find the previous one + RRDHOST *h; + for(h = localhost; h && h->next != host ; h = h->next) ; + + // bypass it + if(h) h->next = host->next; + else error("Request to free RRDHOST '%s': cannot find it", host->hostname); + } + + // ------------------------------------------------------------------------ + // free it + + freez(host->os); + freez(host->cache_dir); + freez(host->varlib_dir); + freez(host->rrdpush_api_key); + freez(host->rrdpush_destination); + freez(host->health_default_exec); + freez(host->health_default_recipient); + freez(host->health_log_filename); + freez(host->hostname); + rrdhost_unlock(host); + netdata_rwlock_destroy(&host->health_log.alarm_log_rwlock); + netdata_rwlock_destroy(&host->rrdhost_rwlock); + freez(host); + + rrd_hosts_available--; +} + +void rrdhost_free_all(void) { + rrd_wrlock(); + while(localhost) rrdhost_free(localhost); + rrd_unlock(); +} + +// ---------------------------------------------------------------------------- +// RRDHOST - save + +void rrdhost_save(RRDHOST *host) { + if(!host) return; + + info("Saving database of host '%s'...", host->hostname); + + RRDSET *st; + + // we get a write lock + // to ensure only one thread is saving the database + rrdhost_wrlock(host); + + rrdset_foreach_write(st, host) { + rrdset_rdlock(st); + rrdset_save(st); + rrdset_unlock(st); + } + + rrdhost_unlock(host); +} + +// ---------------------------------------------------------------------------- +// RRDHOST - delete files + +void rrdhost_delete(RRDHOST *host) { + if(!host) return; + + info("Deleting database of host '%s'...", host->hostname); + + RRDSET *st; + + // we get a write lock + // to ensure only one thread is saving the database + rrdhost_wrlock(host); + + rrdset_foreach_write(st, host) { + rrdset_rdlock(st); + rrdset_delete(st); + rrdset_unlock(st); + } + + rrdhost_unlock(host); +} + +void rrdhost_save_all(void) { + info("Saving database [%zu hosts(s)]...", rrd_hosts_available); + + rrd_rdlock(); + + RRDHOST *host; + rrdhost_foreach_read(host) + rrdhost_save(host); + + rrd_unlock(); +} + +void rrdhost_cleanup_obsolete(RRDHOST *host) { + time_t now = now_realtime_sec(); + + RRDSET *st; + +restart_after_removal: + rrdset_foreach_write(st, host) { + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) + && st->last_accessed_time + rrdset_free_obsolete_time < now + && st->last_updated.tv_sec + rrdset_free_obsolete_time < now + && st->last_collected_time.tv_sec + rrdset_free_obsolete_time < now + )) { + + rrdset_rdlock(st); + + if(rrdhost_flag_check(host, RRDHOST_DELETE_OBSOLETE_FILES)) + rrdset_delete(st); + else + rrdset_save(st); + + rrdset_unlock(st); + + rrdset_free(st); + goto restart_after_removal; + } + } +} diff --git a/src/rrdpush.c b/src/rrdpush.c new file mode 100644 index 000000000..72e6d8a73 --- /dev/null +++ b/src/rrdpush.c @@ -0,0 +1,761 @@ +#include "common.h" + +/* + * rrdpush + * + * 3 threads are involved for all stream operations + * + * 1. a random data collection thread, calling rrdset_done_push() + * this is called for each chart. + * + * the output of this work is kept in a BUFFER in RRDHOST + * the sender thread is signalled via a pipe (also in RRDHOST) + * + * 2. a sender thread running at the sending netdata + * this is spawned automatically on the first chart to be pushed + * + * It tries to push the metrics to the remote netdata, as fast + * as possible (i.e. immediately after they are collected). + * + * 3. a receiver thread, running at the receiving netdata + * this is spawned automatically when the sender connects to + * the receiver. + * + */ + +#define START_STREAMING_PROMPT "Hit me baby, push them over..." + +int default_rrdpush_enabled = 0; +char *default_rrdpush_destination = NULL; +char *default_rrdpush_api_key = NULL; + +int rrdpush_init() { + default_rrdpush_enabled = appconfig_get_boolean(&stream_config, CONFIG_SECTION_STREAM, "enabled", default_rrdpush_enabled); + default_rrdpush_destination = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "destination", ""); + default_rrdpush_api_key = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "api key", ""); + rrdhost_free_orphan_time = config_get_number(CONFIG_SECTION_GLOBAL, "cleanup orphan hosts after seconds", rrdhost_free_orphan_time); + + if(default_rrdpush_enabled && (!default_rrdpush_destination || !*default_rrdpush_destination || !default_rrdpush_api_key || !*default_rrdpush_api_key)) { + error("STREAM [send]: cannot enable sending thread - information is missing."); + default_rrdpush_enabled = 0; + } + + return default_rrdpush_enabled; +} + +#define CONNECTED_TO_SIZE 100 + +// data collection happens from multiple threads +// each of these threads calls rrdset_done() +// which in turn calls rrdset_done_push() +// which uses this pipe to notify the streaming thread +// that there are more data ready to be sent +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +// to have the remote netdata re-sync the charts +// to its current clock, we send for this many +// iterations a BEGIN line without microseconds +// this is for the first iterations of each chart +static unsigned int remote_clock_resync_iterations = 60; + +#define rrdpush_lock(host) netdata_mutex_lock(&((host)->rrdpush_mutex)) +#define rrdpush_unlock(host) netdata_mutex_unlock(&((host)->rrdpush_mutex)) + +// checks if the current chart definition has been sent +static inline int need_to_send_chart_definition(RRDSET *st) { + RRDDIM *rd; + rrddim_foreach_read(rd, st) + if(!rd->exposed) + return 1; + + return 0; +} + +// sends the current chart definition +static inline void send_chart_definition(RRDSET *st) { + buffer_sprintf(st->rrdhost->rrdpush_buffer, "CHART '%s' '%s' '%s' '%s' '%s' '%s' '%s' %ld %d\n" + , st->id + , st->name + , st->title + , st->units + , st->family + , st->context + , rrdset_type_name(st->chart_type) + , st->priority + , st->update_every + ); + + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + buffer_sprintf(st->rrdhost->rrdpush_buffer, "DIMENSION '%s' '%s' '%s' " COLLECTED_NUMBER_FORMAT " " COLLECTED_NUMBER_FORMAT " '%s %s'\n" + , rd->id + , rd->name + , rrd_algorithm_name(rd->algorithm) + , rd->multiplier + , rd->divisor + , rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?"hidden":"" + , rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)?"noreset":"" + ); + rd->exposed = 1; + } +} + +// sends the current chart dimensions +static inline void send_chart_metrics(RRDSET *st) { + buffer_sprintf(st->rrdhost->rrdpush_buffer, "BEGIN %s %llu\n", st->id, (st->counter_done > remote_clock_resync_iterations)?st->usec_since_last_update:0); + + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if(rd->updated && rd->exposed) + buffer_sprintf(st->rrdhost->rrdpush_buffer, "SET %s = " COLLECTED_NUMBER_FORMAT "\n" + , rd->id + , rd->collected_value + ); + } + + buffer_strcat(st->rrdhost->rrdpush_buffer, "END\n"); +} + +void rrdpush_sender_thread_spawn(RRDHOST *host); + +void rrdset_done_push(RRDSET *st) { + RRDHOST *host = st->rrdhost; + + if(unlikely(!rrdset_flag_check(st, RRDSET_FLAG_ENABLED))) + return; + + rrdpush_lock(host); + + if(unlikely(host->rrdpush_enabled && !host->rrdpush_spawn)) + rrdpush_sender_thread_spawn(host); + + if(unlikely(!host->rrdpush_buffer || !host->rrdpush_connected)) { + if(unlikely(!host->rrdpush_error_shown)) + error("STREAM %s [send]: not ready - discarding collected metrics.", host->hostname); + + host->rrdpush_error_shown = 1; + + rrdpush_unlock(host); + return; + } + else if(unlikely(host->rrdpush_error_shown)) { + info("STREAM %s [send]: ready - sending metrics...", host->hostname); + host->rrdpush_error_shown = 0; + } + + if(need_to_send_chart_definition(st)) + send_chart_definition(st); + + send_chart_metrics(st); + + // signal the sender there are more data + if(write(host->rrdpush_pipe[PIPE_WRITE], " ", 1) == -1) + error("STREAM %s [send]: cannot write to internal pipe", host->hostname); + + rrdpush_unlock(host); +} + +// ---------------------------------------------------------------------------- +// rrdpush sender thread + +// resets all the chart, so that their definitions +// will be resent to the central netdata +static void rrdpush_sender_thread_reset_all_charts(RRDHOST *host) { + rrdhost_rdlock(host); + + RRDSET *st; + rrdset_foreach_read(st, host) { + + // make it re-align the current time + // on the remote host + st->counter_done = 0; + + rrdset_rdlock(st); + + RRDDIM *rd; + rrddim_foreach_read(rd, st) + rd->exposed = 0; + + rrdset_unlock(st); + } + + rrdhost_unlock(host); +} + +static inline void rrdpush_sender_thread_data_flush(RRDHOST *host) { + rrdpush_lock(host); + + if(buffer_strlen(host->rrdpush_buffer)) + error("STREAM %s [send]: discarding %zu bytes of metrics already in the buffer.", host->hostname, buffer_strlen(host->rrdpush_buffer)); + + buffer_flush(host->rrdpush_buffer); + + rrdpush_sender_thread_reset_all_charts(host); + + rrdpush_unlock(host); +} + +static void rrdpush_sender_thread_cleanup_locked_all(RRDHOST *host) { + host->rrdpush_connected = 0; + + if(host->rrdpush_socket != -1) { + close(host->rrdpush_socket); + host->rrdpush_socket = -1; + } + + // close the pipe + if(host->rrdpush_pipe[PIPE_READ] != -1) { + close(host->rrdpush_pipe[PIPE_READ]); + host->rrdpush_pipe[PIPE_READ] = -1; + } + + if(host->rrdpush_pipe[PIPE_WRITE] != -1) { + close(host->rrdpush_pipe[PIPE_WRITE]); + host->rrdpush_pipe[PIPE_WRITE] = -1; + } + + buffer_free(host->rrdpush_buffer); + host->rrdpush_buffer = NULL; + + host->rrdpush_spawn = 0; + + rrdhost_flag_set(host, RRDHOST_ORPHAN); +} + +void rrdpush_sender_thread_stop(RRDHOST *host) { + rrdpush_lock(host); + rrdhost_wrlock(host); + + if(host->rrdpush_spawn) { + info("STREAM %s [send]: stopping sending thread...", host->hostname); + pthread_cancel(host->rrdpush_thread); + rrdpush_sender_thread_cleanup_locked_all(host); + } + + rrdhost_unlock(host); + rrdpush_unlock(host); +} + +void *rrdpush_sender_thread(void *ptr) { + RRDHOST *host = (RRDHOST *)ptr; + + info("STREAM %s [send]: thread created (task id %d)", host->hostname, gettid()); + + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("STREAM %s [send]: cannot set pthread cancel type to DEFERRED.", host->hostname); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("STREAM %s [send]: cannot set pthread cancel state to ENABLE.", host->hostname); + + int timeout = (int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "timeout seconds", 60); + int default_port = (int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "default port", 19999); + size_t max_size = (size_t)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "buffer size bytes", 1024 * 1024); + unsigned int reconnect_delay = (unsigned int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "reconnect delay seconds", 5); + remote_clock_resync_iterations = (unsigned int)appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "initial clock resync iterations", remote_clock_resync_iterations); + char connected_to[CONNECTED_TO_SIZE + 1] = ""; + + if(!host->rrdpush_enabled || !host->rrdpush_destination || !*host->rrdpush_destination || !host->rrdpush_api_key || !*host->rrdpush_api_key) + goto cleanup; + + // initialize rrdpush globals + host->rrdpush_buffer = buffer_create(1); + host->rrdpush_connected = 0; + if(pipe(host->rrdpush_pipe) == -1) fatal("STREAM %s [send]: cannot create required pipe.", host->hostname); + + // initialize local variables + size_t begin = 0; + size_t reconnects_counter = 0; + size_t sent_bytes = 0; + size_t sent_connection = 0; + + struct timeval tv = { + .tv_sec = timeout, + .tv_usec = 0 + }; + + struct pollfd fds[2], *ifd, *ofd; + nfds_t fdmax; + + ifd = &fds[0]; + ofd = &fds[1]; + + for(; host->rrdpush_enabled && !netdata_exit ;) { + + if(unlikely(host->rrdpush_socket == -1)) { + // stop appending data into rrdpush_buffer + // they will be lost, so there is no point to do it + host->rrdpush_connected = 0; + + info("STREAM %s [send to %s]: connecting...", host->hostname, host->rrdpush_destination); + host->rrdpush_socket = connect_to_one_of(host->rrdpush_destination, default_port, &tv, &reconnects_counter, connected_to, CONNECTED_TO_SIZE); + + if(unlikely(host->rrdpush_socket == -1)) { + error("STREAM %s [send to %s]: failed to connect", host->hostname, host->rrdpush_destination); + sleep(reconnect_delay); + continue; + } + + info("STREAM %s [send to %s]: initializing communication...", host->hostname, connected_to); + + char http[1000 + 1]; + snprintfz(http, 1000, + "STREAM key=%s&hostname=%s&machine_guid=%s&os=%s&update_every=%d HTTP/1.1\r\n" + "User-Agent: netdata-push-service/%s\r\n" + "Accept: */*\r\n\r\n" + , host->rrdpush_api_key + , host->hostname + , host->machine_guid + , host->os + , default_rrd_update_every + , program_version + ); + + if(send_timeout(host->rrdpush_socket, http, strlen(http), 0, timeout) == -1) { + close(host->rrdpush_socket); + host->rrdpush_socket = -1; + error("STREAM %s [send to %s]: failed to send http header to netdata", host->hostname, connected_to); + sleep(reconnect_delay); + continue; + } + + info("STREAM %s [send to %s]: waiting response from remote netdata...", host->hostname, connected_to); + + if(recv_timeout(host->rrdpush_socket, http, 1000, 0, timeout) == -1) { + close(host->rrdpush_socket); + host->rrdpush_socket = -1; + error("STREAM %s [send to %s]: failed to initialize communication", host->hostname, connected_to); + sleep(reconnect_delay); + continue; + } + + if(strncmp(http, START_STREAMING_PROMPT, strlen(START_STREAMING_PROMPT))) { + close(host->rrdpush_socket); + host->rrdpush_socket = -1; + error("STREAM %s [send to %s]: server is not replying properly.", host->hostname, connected_to); + sleep(reconnect_delay); + continue; + } + + info("STREAM %s [send to %s]: established communication - sending metrics...", host->hostname, connected_to); + + if(fcntl(host->rrdpush_socket, F_SETFL, O_NONBLOCK) < 0) + error("STREAM %s [send to %s]: cannot set non-blocking mode for socket.", host->hostname, connected_to); + + rrdpush_sender_thread_data_flush(host); + sent_connection = 0; + + // allow appending data into rrdpush_buffer + host->rrdpush_connected = 1; + } + + ifd->fd = host->rrdpush_pipe[PIPE_READ]; + ifd->events = POLLIN; + ifd->revents = 0; + + ofd->fd = host->rrdpush_socket; + ofd->revents = 0; + if(begin < buffer_strlen(host->rrdpush_buffer)) { + ofd->events = POLLOUT; + fdmax = 2; + } + else { + ofd->events = 0; + fdmax = 1; + } + + if(netdata_exit) break; + int retval = poll(fds, fdmax, timeout * 1000); + if(netdata_exit) break; + + if(unlikely(retval == -1)) { + if(errno == EAGAIN || errno == EINTR) + continue; + + error("STREAM %s [send to %s]: failed to poll().", host->hostname, connected_to); + close(host->rrdpush_socket); + host->rrdpush_socket = -1; + break; + } + else if(unlikely(!retval)) { + // timeout + continue; + } + + if(ifd->revents & POLLIN) { + char buffer[1000 + 1]; + if(read(host->rrdpush_pipe[PIPE_READ], buffer, 1000) == -1) + error("STREAM %s [send to %s]: cannot read from internal pipe.", host->hostname, connected_to); + } + + if(ofd->revents & POLLOUT && begin < buffer_strlen(host->rrdpush_buffer)) { + + // BEGIN RRDPUSH LOCKED SESSION + + // during this session, data collectors + // will not be able to append data to our buffer + // but the socket is in non-blocking mode + // so, we will not block at send() + + if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0) + error("STREAM %s [send]: cannot set pthread cancel state to DISABLE.", host->hostname); + + rrdpush_lock(host); + + ssize_t ret = send(host->rrdpush_socket, &host->rrdpush_buffer->buffer[begin], buffer_strlen(host->rrdpush_buffer) - begin, MSG_DONTWAIT); + if(ret == -1) { + if(errno != EAGAIN && errno != EINTR) { + error("STREAM %s [send to %s]: failed to send metrics - closing connection - we have sent %zu bytes on this connection.", host->hostname, connected_to, sent_connection); + close(host->rrdpush_socket); + host->rrdpush_socket = -1; + } + } + else { + sent_connection += ret; + sent_bytes += ret; + begin += ret; + if(begin == buffer_strlen(host->rrdpush_buffer)) { + // we send it all + + buffer_flush(host->rrdpush_buffer); + begin = 0; + } + } + + rrdpush_unlock(host); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("STREAM %s [send]: cannot set pthread cancel state to ENABLE.", host->hostname); + + // END RRDPUSH LOCKED SESSION + } + + // protection from overflow + if(host->rrdpush_buffer->len > max_size) { + errno = 0; + error("STREAM %s [send to %s]: too many data pending - buffer is %zu bytes long, %zu unsent - we have sent %zu bytes in total, %zu on this connection. Closing connection to flush the data.", host->hostname, connected_to, host->rrdpush_buffer->len, host->rrdpush_buffer->len - begin, sent_bytes, sent_connection); + if(host->rrdpush_socket != -1) { + close(host->rrdpush_socket); + host->rrdpush_socket = -1; + } + } + } + +cleanup: + debug(D_WEB_CLIENT, "STREAM %s [send]: sending thread exits.", host->hostname); + + if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0) + error("STREAM %s [send]: cannot set pthread cancel state to DISABLE.", host->hostname); + + rrdpush_lock(host); + rrdhost_wrlock(host); + rrdpush_sender_thread_cleanup_locked_all(host); + rrdhost_unlock(host); + rrdpush_unlock(host); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("STREAM %s [send]: cannot set pthread cancel state to ENABLE.", host->hostname); + + pthread_exit(NULL); + return NULL; +} + + +// ---------------------------------------------------------------------------- +// rrdpush receiver thread + +int rrdpush_receive(int fd, const char *key, const char *hostname, const char *machine_guid, const char *os, int update_every, char *client_ip, char *client_port) { + RRDHOST *host; + int history = default_rrd_history_entries; + RRD_MEMORY_MODE mode = default_rrd_memory_mode; + int health_enabled = default_health_enabled; + int rrdpush_enabled = default_rrdpush_enabled; + char *rrdpush_destination = default_rrdpush_destination; + char *rrdpush_api_key = default_rrdpush_api_key; + time_t alarms_delay = 60; + + update_every = (int)appconfig_get_number(&stream_config, machine_guid, "update every", update_every); + if(update_every < 0) update_every = 1; + + history = (int)appconfig_get_number(&stream_config, key, "default history", history); + history = (int)appconfig_get_number(&stream_config, machine_guid, "history", history); + if(history < 5) history = 5; + + mode = rrd_memory_mode_id(appconfig_get(&stream_config, key, "default memory mode", rrd_memory_mode_name(mode))); + mode = rrd_memory_mode_id(appconfig_get(&stream_config, machine_guid, "memory mode", rrd_memory_mode_name(mode))); + + health_enabled = appconfig_get_boolean_ondemand(&stream_config, key, "health enabled by default", health_enabled); + health_enabled = appconfig_get_boolean_ondemand(&stream_config, machine_guid, "health enabled", health_enabled); + + alarms_delay = appconfig_get_number(&stream_config, key, "default postpone alarms on connect seconds", alarms_delay); + alarms_delay = appconfig_get_number(&stream_config, machine_guid, "postpone alarms on connect seconds", alarms_delay); + + rrdpush_enabled = appconfig_get_boolean(&stream_config, key, "default proxy enabled", rrdpush_enabled); + rrdpush_enabled = appconfig_get_boolean(&stream_config, machine_guid, "proxy enabled", rrdpush_enabled); + + rrdpush_destination = appconfig_get(&stream_config, key, "default proxy destination", rrdpush_destination); + rrdpush_destination = appconfig_get(&stream_config, machine_guid, "proxy destination", rrdpush_destination); + + rrdpush_api_key = appconfig_get(&stream_config, key, "default proxy api key", rrdpush_api_key); + rrdpush_api_key = appconfig_get(&stream_config, machine_guid, "proxy api key", rrdpush_api_key); + + if(!strcmp(machine_guid, "localhost")) + host = localhost; + else + host = rrdhost_find_or_create( + hostname + , machine_guid + , os + , update_every + , history + , mode + , (health_enabled != CONFIG_BOOLEAN_NO) + , (rrdpush_enabled && rrdpush_destination && *rrdpush_destination && rrdpush_api_key && *rrdpush_api_key) + , rrdpush_destination + , rrdpush_api_key + ); + + if(!host) { + close(fd); + error("STREAM %s [receive from [%s]:%s]: failed to find/create host structure.", hostname, client_ip, client_port); + return 1; + } + +#ifdef NETDATA_INTERNAL_CHECKS + info("STREAM %s [receive from [%s]:%s]: client willing to stream metrics for host '%s' with machine_guid '%s': update every = %d, history = %ld, memory mode = %s, health %s" + , hostname + , client_ip + , client_port + , host->hostname + , host->machine_guid + , host->rrd_update_every + , host->rrd_history_entries + , rrd_memory_mode_name(host->rrd_memory_mode) + , (health_enabled == CONFIG_BOOLEAN_NO)?"disabled":((health_enabled == CONFIG_BOOLEAN_YES)?"enabled":"auto") + ); +#endif // NETDATA_INTERNAL_CHECKS + + struct plugind cd = { + .enabled = 1, + .update_every = default_rrd_update_every, + .pid = 0, + .serial_failures = 0, + .successful_collections = 0, + .obsolete = 0, + .started_t = now_realtime_sec(), + .next = NULL, + }; + + // put the client IP and port into the buffers used by plugins.d + snprintfz(cd.id, CONFIG_MAX_NAME, "%s:%s", client_ip, client_port); + snprintfz(cd.filename, FILENAME_MAX, "%s:%s", client_ip, client_port); + snprintfz(cd.fullfilename, FILENAME_MAX, "%s:%s", client_ip, client_port); + snprintfz(cd.cmd, PLUGINSD_CMD_MAX, "%s:%s", client_ip, client_port); + + info("STREAM %s [receive from [%s]:%s]: initializing communication...", host->hostname, client_ip, client_port); + if(send_timeout(fd, START_STREAMING_PROMPT, strlen(START_STREAMING_PROMPT), 0, 60) != strlen(START_STREAMING_PROMPT)) { + error("STREAM %s [receive from [%s]:%s]: cannot send ready command.", host->hostname, client_ip, client_port); + close(fd); + return 0; + } + + // remove the non-blocking flag from the socket + if(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK) == -1) + error("STREAM %s [receive from [%s]:%s]: cannot remove the non-blocking flag from socket %d", host->hostname, client_ip, client_port, fd); + + // convert the socket to a FILE * + FILE *fp = fdopen(fd, "r"); + if(!fp) { + error("STREAM %s [receive from [%s]:%s]: failed to get a FILE for FD %d.", host->hostname, client_ip, client_port, fd); + close(fd); + return 0; + } + + rrdhost_wrlock(host); + host->connected_senders++; + if(health_enabled != CONFIG_BOOLEAN_NO) + host->health_delay_up_to = now_realtime_sec() + alarms_delay; + rrdhost_unlock(host); + + // call the plugins.d processor to receive the metrics + info("STREAM %s [receive from [%s]:%s]: receiving metrics...", host->hostname, client_ip, client_port); + size_t count = pluginsd_process(host, &cd, fp, 1); + error("STREAM %s [receive from [%s]:%s]: disconnected (completed updates %zu).", host->hostname, client_ip, client_port, count); + + rrdhost_wrlock(host); + host->senders_disconnected_time = now_realtime_sec(); + host->connected_senders--; + if(!host->connected_senders) { + if(health_enabled == CONFIG_BOOLEAN_AUTO) + host->health_enabled = 0; + } + rrdhost_unlock(host); + + rrdpush_sender_thread_stop(host); + + // cleanup + fclose(fp); + + return (int)count; +} + +struct rrdpush_thread { + int fd; + char *key; + char *hostname; + char *machine_guid; + char *os; + char *client_ip; + char *client_port; + int update_every; +}; + +void *rrdpush_receiver_thread(void *ptr) { + struct rrdpush_thread *rpt = (struct rrdpush_thread *)ptr; + + if (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("STREAM %s [receive]: cannot set pthread cancel type to DEFERRED.", rpt->hostname); + + if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("STREAM %s [receive]: cannot set pthread cancel state to ENABLE.", rpt->hostname); + + + info("STREAM %s [%s]:%s: receive thread created (task id %d)", rpt->hostname, rpt->client_ip, rpt->client_port, gettid()); + rrdpush_receive(rpt->fd, rpt->key, rpt->hostname, rpt->machine_guid, rpt->os, rpt->update_every, rpt->client_ip, rpt->client_port); + info("STREAM %s [receive from [%s]:%s]: receive thread ended (task id %d)", rpt->hostname, rpt->client_ip, rpt->client_port, gettid()); + + freez(rpt->key); + freez(rpt->hostname); + freez(rpt->machine_guid); + freez(rpt->os); + freez(rpt->client_ip); + freez(rpt->client_port); + freez(rpt); + + pthread_exit(NULL); + return NULL; +} + +void rrdpush_sender_thread_spawn(RRDHOST *host) { + rrdhost_wrlock(host); + + if(!host->rrdpush_spawn) { + if(pthread_create(&host->rrdpush_thread, NULL, rrdpush_sender_thread, (void *) host)) + error("STREAM %s [send]: failed to create new thread for client.", host->hostname); + + else if(pthread_detach(host->rrdpush_thread)) + error("STREAM %s [send]: cannot request detach newly created thread.", host->hostname); + + rrdhost_flag_clear(host, RRDHOST_ORPHAN); + host->rrdpush_spawn = 1; + } + + rrdhost_unlock(host); +} + +int rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url) { + (void)host; + + info("STREAM [receive from [%s]:%s]: new client connection.", w->client_ip, w->client_port); + + char *key = NULL, *hostname = NULL, *machine_guid = NULL, *os = "unknown"; + int update_every = default_rrd_update_every; + char buf[GUID_LEN + 1]; + + while(url) { + char *value = mystrsep(&url, "?&"); + if(!value || !*value) continue; + + char *name = mystrsep(&value, "="); + if(!name || !*name) continue; + if(!value || !*value) continue; + + if(!strcmp(name, "key")) + key = value; + else if(!strcmp(name, "hostname")) + hostname = value; + else if(!strcmp(name, "machine_guid")) + machine_guid = value; + else if(!strcmp(name, "update_every")) + update_every = (int)strtoul(value, NULL, 0); + else if(!strcmp(name, "os")) + os = value; + } + + if(!key || !*key) { + error("STREAM [receive from [%s]:%s]: request without an API key. Forbidding access.", w->client_ip, w->client_port); + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "You need an API key for this request."); + return 401; + } + + if(!hostname || !*hostname) { + error("STREAM [receive from [%s]:%s]: request without a hostname. Forbidding access.", w->client_ip, w->client_port); + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "You need to send a hostname too."); + return 400; + } + + if(!machine_guid || !*machine_guid) { + error("STREAM [receive from [%s]:%s]: request without a machine GUID. Forbidding access.", w->client_ip, w->client_port); + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "You need to send a machine GUID too."); + return 400; + } + + if(regenerate_guid(key, buf) == -1) { + error("STREAM [receive from [%s]:%s]: API key '%s' is not valid GUID. Forbidding access.", w->client_ip, w->client_port, key); + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Your API key is invalid."); + return 401; + } + + if(regenerate_guid(machine_guid, buf) == -1) { + error("STREAM [receive from [%s]:%s]: machine GUID '%s' is not GUID. Forbidding access.", w->client_ip, w->client_port, key); + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Your machine GUID is invalid."); + return 404; + } + + if(!appconfig_get_boolean(&stream_config, key, "enabled", 0)) { + error("STREAM [receive from [%s]:%s]: API key '%s' is not allowed. Forbidding access.", w->client_ip, w->client_port, machine_guid); + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Your API key is not permitted access."); + return 401; + } + + if(!appconfig_get_boolean(&stream_config, machine_guid, "enabled", 1)) { + error("STREAM [receive from [%s]:%s]: machine GUID '%s' is not allowed. Forbidding access.", w->client_ip, w->client_port, machine_guid); + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Your machine guide is not permitted access."); + return 404; + } + + struct rrdpush_thread *rpt = mallocz(sizeof(struct rrdpush_thread)); + rpt->fd = w->ifd; + rpt->key = strdupz(key); + rpt->hostname = strdupz(hostname); + rpt->machine_guid = strdupz(machine_guid); + rpt->os = strdupz(os); + rpt->client_ip = strdupz(w->client_ip); + rpt->client_port = strdupz(w->client_port); + rpt->update_every = update_every; + pthread_t thread; + + debug(D_SYSTEM, "STREAM [receive from [%s]:%s]: starting receiving thread.", w->client_ip, w->client_port); + + if(pthread_create(&thread, NULL, rrdpush_receiver_thread, (void *)rpt)) + error("STREAM [receive from [%s]:%s]: failed to create new thread for client.", w->client_ip, w->client_port); + + else if(pthread_detach(thread)) + error("STREAM [receive from [%s]:%s]: cannot request detach newly created thread.", w->client_ip, w->client_port); + + // prevent the caller from closing the streaming socket + if(w->ifd == w->ofd) + w->ifd = w->ofd = -1; + else + w->ifd = -1; + + buffer_flush(w->response.data); + return 200; +} diff --git a/src/rrdpush.h b/src/rrdpush.h new file mode 100644 index 000000000..dddbe758b --- /dev/null +++ b/src/rrdpush.h @@ -0,0 +1,15 @@ +#ifndef NETDATA_RRDPUSH_H +#define NETDATA_RRDPUSH_H + +extern int default_rrdpush_enabled; +extern char *default_rrdpush_destination; +extern char *default_rrdpush_api_key; + +extern int rrdpush_init(); +extern void rrdset_done_push(RRDSET *st); +extern void *rrdpush_sender_thread(void *ptr); + +extern int rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url); +extern void rrdpush_sender_thread_stop(RRDHOST *host); + +#endif //NETDATA_RRDPUSH_H diff --git a/src/rrdset.c b/src/rrdset.c new file mode 100644 index 000000000..c847b9690 --- /dev/null +++ b/src/rrdset.c @@ -0,0 +1,1310 @@ +#define NETDATA_RRD_INTERNALS 1 +#include "common.h" + +#define RRD_DEFAULT_GAP_INTERPOLATIONS 1 + +void __rrdset_check_rdlock(RRDSET *st, const char *file, const char *function, const unsigned long line) { + debug(D_RRD_CALLS, "Checking read lock on chart '%s'", st->id); + + int ret = netdata_rwlock_trywrlock(&st->rrdset_rwlock); + if(ret == 0) + fatal("RRDSET '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", st->id, function, line, file); +} + +void __rrdset_check_wrlock(RRDSET *st, const char *file, const char *function, const unsigned long line) { + debug(D_RRD_CALLS, "Checking write lock on chart '%s'", st->id); + + int ret = netdata_rwlock_tryrdlock(&st->rrdset_rwlock); + if(ret == 0) + fatal("RRDSET '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", st->id, function, line, file); +} + + +// ---------------------------------------------------------------------------- +// RRDSET index + +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); +} + +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(&(host->rrdset_root_index), (avl *) &tmp); +} + +// ---------------------------------------------------------------------------- +// RRDSET name index + +#define rrdset_from_avlname(avlname_ptr) ((RRDSET *)((avlname_ptr) - offsetof(RRDSET, avlname))) + +int rrdset_compare_name(void* a, void* b) { + RRDSET *A = rrdset_from_avlname(a); + RRDSET *B = rrdset_from_avlname(b); + + // 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); +} + +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; +} + +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; +} + + +// ---------------------------------------------------------------------------- +// RRDSET - find charts + +static inline 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; +} + +inline RRDSET *rrdset_find(RRDHOST *host, const char *id) { + debug(D_RRD_CALLS, "rrdset_find() for chart '%s' in host '%s'", id, host->hostname); + RRDSET *st = rrdset_index_find(host, id, 0); + return(st); +} + +inline RRDSET *rrdset_find_bytype(RRDHOST *host, const char *type, const char *id) { + debug(D_RRD_CALLS, "rrdset_find_bytype() for chart '%s.%s' in host '%s'", type, id, host->hostname); + + 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)); + + return(rrdset_find(host, buf)); +} + +inline RRDSET *rrdset_find_byname(RRDHOST *host, const char *name) { + debug(D_RRD_CALLS, "rrdset_find_byname() for chart '%s' in host '%s'", name, host->hostname); + RRDSET *st = rrdset_index_find_name(host, name, 0); + return(st); +} + +// ---------------------------------------------------------------------------- +// RRDSET - rename charts + +char *rrdset_strncpyz_name(char *to, const char *from, size_t length) { + 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) { + if(unlikely(st->name && !strcmp(st->name, name))) + return; + + debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name); + + 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_strncpyz_name(b, n, CONFIG_MAX_VALUE); + + if(st->name) { + rrdset_index_del_name(st->rrdhost, st); + st->name = config_set_default(st->config_section, "name", b); + st->hash_name = simple_hash(st->name); + rrdsetvar_rename_all(st); + } + else { + st->name = config_get(st->config_section, "name", b); + st->hash_name = simple_hash(st->name); + } + + rrdset_wrlock(st); + RRDDIM *rd; + rrddim_foreach_write(rd, st) + rrddimvar_rename_all(rd); + rrdset_unlock(st); + + if(unlikely(rrdset_index_add_name(st->rrdhost, st) != st)) + error("RRDSET: INTERNAL ERROR: attempted to index duplicate chart name '%s'", st->name); +} + + +// ---------------------------------------------------------------------------- +// RRDSET - reset a chart + +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; + rrddim_foreach_read(rd, st) { + rd->last_collected_time.tv_sec = 0; + rd->last_collected_time.tv_usec = 0; + rd->collections_counter = 0; + memset(rd->values, 0, rd->entries * sizeof(storage_number)); + } +} + +// ---------------------------------------------------------------------------- +// RRDSET - helpers for rrdset_create() + +inline long align_entries_to_pagesize(RRD_MEMORY_MODE mode, long entries) { + if(unlikely(entries < 5)) entries = 5; + if(unlikely(entries > RRD_HISTORY_ENTRIES_MAX)) entries = RRD_HISTORY_ENTRIES_MAX; + + if(unlikely(mode == RRD_MEMORY_MODE_NONE || mode == RRD_MEMORY_MODE_RAM)) + return entries; + + long page = (size_t)sysconf(_SC_PAGESIZE); + long size = sizeof(RRDDIM) + entries * sizeof(storage_number); + if(unlikely(size % page)) { + size -= (size % page); + size += page; + + long n = (size - sizeof(RRDDIM)) / sizeof(storage_number); + return n; + } + + return entries; +} + +static inline void last_collected_time_align(struct timeval *tv, int update_every) { + tv->tv_sec -= tv->tv_sec % update_every; + tv->tv_usec = 500000; +} + +static inline void last_updated_time_align(struct timeval *tv, int update_every) { + tv->tv_sec -= tv->tv_sec % update_every; + tv->tv_usec = 0; +} + +// ---------------------------------------------------------------------------- +// RRDSET - free a chart + +void rrdset_free(RRDSET *st) { + if(unlikely(!st)) return; + + rrdhost_check_wrlock(st->rrdhost); // make sure we have a write lock on the host + rrdset_wrlock(st); // lock this RRDSET + + // info("Removing chart '%s' ('%s')", st->id, st->name); + + // ------------------------------------------------------------------------ + // remove it from the indexes + + if(unlikely(rrdset_index_del(st->rrdhost, st) != st)) + error("RRDSET: INTERNAL ERROR: attempt to remove from index chart '%s', removed a different chart.", st->id); + + rrdset_index_del_name(st->rrdhost, st); + + // ------------------------------------------------------------------------ + // free its children structures + + while(st->variables) rrdsetvar_free(st->variables); + while(st->alarms) rrdsetcalc_unlink(st->alarms); + while(st->dimensions) rrddim_free(st, st->dimensions); + + rrdfamily_free(st->rrdhost, st->rrdfamily); + + // ------------------------------------------------------------------------ + // unlink it from the host + + if(st == st->rrdhost->rrdset_root) { + st->rrdhost->rrdset_root = st->next; + } + else { + // find the previous one + RRDSET *s; + for(s = st->rrdhost->rrdset_root; s && s->next != st ; s = s->next) ; + + // bypass it + if(s) s->next = st->next; + else error("Request to free RRDSET '%s': cannot find it under host '%s'", st->id, st->rrdhost->hostname); + } + + rrdset_unlock(st); + + // ------------------------------------------------------------------------ + // free it + + netdata_rwlock_destroy(&st->rrdset_rwlock); + + // free directly allocated members + freez(st->config_section); + + if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { + debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name); + munmap(st, st->memsize); + } + else + freez(st); +} + +void rrdset_save(RRDSET *st) { + RRDDIM *rd; + + rrdset_check_rdlock(st); + + // info("Saving chart '%s' ('%s')", st->id, st->name); + + if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) { + debug(D_RRD_STATS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename); + savememory(st->cache_filename, st, st->memsize); + } + + rrddim_foreach_read(rd, st) { + if(likely(rd->rrd_memory_mode == RRD_MEMORY_MODE_SAVE)) { + debug(D_RRD_STATS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename); + savememory(rd->cache_filename, rd, rd->memsize); + } + } +} + +void rrdset_delete(RRDSET *st) { + RRDDIM *rd; + + rrdset_check_rdlock(st); + + // info("Deleting chart '%s' ('%s')", st->id, st->name); + + if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE) { + debug(D_RRD_STATS, "Deleting stats '%s' to '%s'.", st->name, st->cache_filename); + unlink(st->cache_filename); + } + + rrddim_foreach_read(rd, st) { + if(likely(rd->rrd_memory_mode == RRD_MEMORY_MODE_SAVE)) { + debug(D_RRD_STATS, "Deleting dimension '%s' to '%s'.", rd->name, rd->cache_filename); + unlink(rd->cache_filename); + } + } +} + +// ---------------------------------------------------------------------------- +// RRDSET - create a chart + +static inline RRDSET *rrdset_find_on_create(RRDHOST *host, const char *fullid) { + RRDSET *st = rrdset_find(host, fullid); + if(unlikely(st)) { + rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); + debug(D_RRD_CALLS, "RRDSET '%s', already exists.", fullid); + return st; + } + + return NULL; +} + +RRDSET *rrdset_create( + RRDHOST *host + , 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 + , RRDSET_TYPE 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; + } + + // ------------------------------------------------------------------------ + // check if it already exists + + char fullid[RRD_ID_LENGTH_MAX + 1]; + snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id); + + RRDSET *st = rrdset_find_on_create(host, fullid); + if(st) return st; + + rrdhost_wrlock(host); + + st = rrdset_find_on_create(host, fullid); + if(st) { + rrdhost_unlock(host); + return st; + } + + char fullfilename[FILENAME_MAX + 1]; + + // ------------------------------------------------------------------------ + // compose the config_section for this chart + + char config_section[RRD_ID_LENGTH_MAX + 1]; + if(host == localhost) + strcpy(config_section, fullid); + else + snprintfz(config_section, RRD_ID_LENGTH_MAX, "%s/%s", host->machine_guid, fullid); + + // ------------------------------------------------------------------------ + // get the options from the config, we need to create it + + long rentries = config_get_number(config_section, "history", host->rrd_history_entries); + long entries = align_entries_to_pagesize(host->rrd_memory_mode, rentries); + if(entries != rentries) entries = config_set_number(config_section, "history", entries); + + if(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE && entries != rentries) + entries = config_set_number(config_section, "history", 10); + + int enabled = config_get_boolean(config_section, "enabled", 1); + if(!enabled) entries = 5; + + unsigned long size = sizeof(RRDSET); + char *cache_dir = rrdset_cache_dir(host, fullid, config_section); + + time_t now = now_realtime_sec(); + + // ------------------------------------------------------------------------ + // load it or allocate it + + debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id); + + snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir); + if(host->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { + st = (RRDSET *) mymmap(fullfilename, size, ((host->rrd_memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE), 0); + if(st) { + memset(&st->avl, 0, sizeof(avl)); + memset(&st->avlname, 0, sizeof(avl)); + memset(&st->variables_root_index, 0, sizeof(avl_tree_lock)); + memset(&st->dimensions_index, 0, sizeof(avl_tree_lock)); + memset(&st->rrdset_rwlock, 0, sizeof(netdata_rwlock_t)); + + 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->variables = NULL; + st->alarms = NULL; + st->flags = 0x00000000; + + if(strcmp(st->magic, RRDSET_MAGIC) != 0) { + errno = 0; + info("Initializing file %s.", fullfilename); + memset(st, 0, 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; + memset(st, 0, size); + } + else if(st->memsize != size || st->entries != entries) { + errno = 0; + error("File %s does not have the desired size. Clearing it.", fullfilename); + memset(st, 0, size); + } + else if(st->update_every != update_every) { + errno = 0; + error("File %s does not have the desired update frequency. Clearing it.", fullfilename); + memset(st, 0, size); + } + else if((now - st->last_updated.tv_sec) > update_every * entries) { + errno = 0; + error("File %s is too old. Clearing it.", fullfilename); + memset(st, 0, size); + } + else if(st->last_updated.tv_sec > now + update_every) { + errno = 0; + error("File %s refers to the future. Clearing it.", fullfilename); + memset(st, 0, size); + } + + // make sure the database is aligned + if(st->last_updated.tv_sec) + last_updated_time_align(&st->last_updated, update_every); + + + // make sure we have the right memory mode + // even if we cleared the memory + st->rrd_memory_mode = host->rrd_memory_mode; + } + } + + if(unlikely(!st)) { + st = callocz(1, size); + st->rrd_memory_mode = (host->rrd_memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_RAM; + } + + st->config_section = strdup(config_section); + st->rrdhost = host; + 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->config_section, "chart type", rrdset_type_name(chart_type))); + st->type = config_get(st->config_section, "type", type); + st->family = config_get(st->config_section, "family", family?family:st->type); + st->units = config_get(st->config_section, "units", units?units:""); + + st->context = config_get(st->config_section, "context", context?context:st->id); + st->hash_context = simple_hash(st->context); + + st->priority = config_get_number(st->config_section, "priority", priority); + if(enabled) + rrdset_flag_set(st, RRDSET_FLAG_ENABLED); + else + rrdset_flag_clear(st, RRDSET_FLAG_ENABLED); + + rrdset_flag_clear(st, RRDSET_FLAG_DETAIL); + rrdset_flag_clear(st, RRDSET_FLAG_DEBUG); + rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); + + // 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->config_section, "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); + + netdata_rwlock_init(&st->rrdset_rwlock); + + if(name && *name) rrdset_set_name(st, name); + else rrdset_set_name(st, id); + + { + char varvalue[CONFIG_MAX_VALUE + 1]; + char varvalue2[CONFIG_MAX_VALUE + 1]; + snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name); + json_escape_string(varvalue2, varvalue, sizeof(varvalue2)); + st->title = config_get(st->config_section, "title", varvalue2); + } + + st->rrdfamily = rrdfamily_create(host, st->family); + + st->next = host->rrdset_root; + host->rrdset_root = st; + + if(host->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); + } + + if(unlikely(rrdset_index_add(host, st) != st)) + error("RRDSET: INTERNAL ERROR: attempt to index duplicate chart '%s'", st->id); + + rrdsetcalc_link_matching(st); + rrdcalctemplate_link_matching(st); + + rrdhost_cleanup_obsolete(host); + + rrdhost_unlock(host); + + return(st); +} + + +// ---------------------------------------------------------------------------- +// RRDSET - data collection iteration control + +inline void rrdset_next_usec_unfiltered(RRDSET *st, usec_t microseconds) { + + if(unlikely(!st->last_collected_time.tv_sec)) { + // the first entry + microseconds = st->update_every * USEC_PER_SEC; + } + else if(unlikely(!microseconds)) { + // no dt given by the plugin + struct timeval now; + now_realtime_timeval(&now); + microseconds = dt_usec(&now, &st->last_collected_time); + } + + st->usec_since_last_update = microseconds; +} + +inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) { + struct timeval now; + now_realtime_timeval(&now); + + if(unlikely(!st->last_collected_time.tv_sec)) { + // the first entry + microseconds = st->update_every * USEC_PER_SEC; + } + else if(unlikely(!microseconds)) { + // no dt given by the plugin + microseconds = dt_usec(&now, &st->last_collected_time); + } + else { + // microseconds has the time since the last collection +//#ifdef NETDATA_INTERNAL_CHECKS +// usec_t now_usec = timeval_usec(&now); +// usec_t last_usec = timeval_usec(&st->last_collected_time); +//#endif + susec_t since_last_usec = dt_usec_signed(&now, &st->last_collected_time); + + if(unlikely(since_last_usec < 0)) { + // oops! the database is in the future + error("Database for chart '%s' on host '%s' is %lld microseconds in the future. Adjusting it to current time.", st->id, st->rrdhost->hostname, -since_last_usec); + + st->last_collected_time.tv_sec = now.tv_sec - st->update_every; + st->last_collected_time.tv_usec = now.tv_usec; + last_collected_time_align(&st->last_collected_time, st->update_every); + + st->last_updated.tv_sec = now.tv_sec - st->update_every; + st->last_updated.tv_usec = now.tv_usec; + last_updated_time_align(&st->last_updated, st->update_every); + + microseconds = st->update_every * USEC_PER_SEC; + since_last_usec = st->update_every * USEC_PER_SEC; + } + + // verify the microseconds given is good + if(unlikely(microseconds > (usec_t)since_last_usec)) { + debug(D_RRD_CALLS, "dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - (usec_t)since_last_usec, st->name, st->id); + +//#ifdef NETDATA_INTERNAL_CHECKS +// if(unlikely(last_usec + microseconds > now_usec + 1000)) +// error("dt %llu usec given is too big - it leads %llu usec to the future, for chart '%s' (%s).", microseconds, microseconds - (usec_t)since_last_usec, st->name, st->id); +//#endif + + microseconds = (usec_t)since_last_usec; + } + else if(unlikely(microseconds < (usec_t)since_last_usec * 0.8)) { + debug(D_RRD_CALLS, "dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, (usec_t)since_last_usec, st->name, st->id); + +//#ifdef NETDATA_INTERNAL_CHECKS +// error("dt %llu usec given is too small - expected %llu usec up to -20%%, for chart '%s' (%s).", microseconds, (usec_t)since_last_usec, st->name, st->id); +//#endif + microseconds = (usec_t)since_last_usec; + } + } + debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds); + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) + debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds); + + st->usec_since_last_update = microseconds; +} + + +// ---------------------------------------------------------------------------- +// RRDSET - process the collected values for all dimensions of a chart + +static inline void rrdset_init_last_collected_time(RRDSET *st) { + now_realtime_timeval(&st->last_collected_time); + last_collected_time_align(&st->last_collected_time, st->update_every); +} + +static inline usec_t rrdset_update_last_collected_time(RRDSET *st) { + usec_t last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec; + usec_t ut = last_collect_ut + st->usec_since_last_update; + st->last_collected_time.tv_sec = (time_t) (ut / USEC_PER_SEC); + st->last_collected_time.tv_usec = (suseconds_t) (ut % USEC_PER_SEC); + return last_collect_ut; +} + +static inline void rrdset_init_last_updated_time(RRDSET *st) { + // copy the last collected time to last updated time + st->last_updated.tv_sec = st->last_collected_time.tv_sec; + st->last_updated.tv_usec = st->last_collected_time.tv_usec; + last_updated_time_align(&st->last_updated, st->update_every); +} + +static inline void rrdset_done_push_exclusive(RRDSET *st) { + if(unlikely(!st->last_collected_time.tv_sec)) { + // it is the first entry + // set the last_collected_time to now + rrdset_init_last_collected_time(st); + } + else { + // it is not the first entry + // calculate the proper last_collected_time, using usec_since_last_update + rrdset_update_last_collected_time(st); + } + + st->counter_done++; + + rrdset_rdlock(st); + rrdset_done_push(st); + rrdset_unlock(st); +} + +void rrdset_done(RRDSET *st) { + if(unlikely(netdata_exit)) return; + + if(unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) { + if(unlikely(st->rrdhost->rrdpush_enabled)) + rrdset_done_push_exclusive(st); + + return; + } + + debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name); + + RRDDIM *rd; + + int + pthreadoldcancelstate; // store the old cancelable pthread state, to restore it at the end + + char + store_this_entry = 1, // boolean: 1 = store this entry, 0 = don't store this entry + first_entry = 0; // boolean: 1 = this is the first entry seen for this chart, 0 = all other entries + + unsigned int + stored_entries = 0; // the number of entries we have stored in the db, during this call to rrdset_done() + + usec_t + last_collect_ut, // the timestamp in microseconds, of the last collected value + now_collect_ut, // the timestamp in microseconds, of this collected value (this is NOW) + last_stored_ut, // the timestamp in microseconds, of the last stored entry in the db + next_store_ut, // the timestamp in microseconds, of the next entry to store in the db + update_every_ut = st->update_every * USEC_PER_SEC; // st->update_every in microseconds + + if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 0)) + error("Cannot set pthread cancel state to DISABLE."); + + // a read lock is OK here + rrdset_rdlock(st); + +/* + // enable the chart, if it was disabled + if(unlikely(rrd_delete_unupdated_dimensions) && !st->enabled) + st->enabled = 1; +*/ + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE))) { + error("Chart '%s' has the OBSOLETE flag set, but it is collected.", st->id); + rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); + } + + // check if the chart has a long time to be updated + if(unlikely(st->usec_since_last_update > st->entries * update_every_ut)) { + info("%s: took too long to be updated (%0.3Lf secs). Resetting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0)); + rrdset_reset(st); + st->usec_since_last_update = update_every_ut; + first_entry = 1; + } + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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 + rrdset_init_last_collected_time(st); + + last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec - update_every_ut; + + // the first entry should not be stored + store_this_entry = 0; + first_entry = 1; + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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 + last_collect_ut = rrdset_update_last_collected_time(st); + } + + // 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 + rrdset_init_last_updated_time(st); + + // the first entry should not be stored + store_this_entry = 0; + first_entry = 1; + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) + debug(D_RRD_STATS, "%s: initializing last_updated to last_collected_time - %llu microseconds. Will not store the next entry.", st->name, st->usec_since_last_update); + } + + // check if we will re-write the entire data set + if(unlikely(dt_usec(&st->last_collected_time, &st->last_updated) > st->entries * update_every_ut)) { + info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Resetting 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); + rrdset_init_last_updated_time(st); + + st->usec_since_last_update = update_every_ut; + + // 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_stored_ut = the last time we added a value to the storage + // now_collect_ut = the time the current value has been collected + // next_store_ut = the time of the next interpolation point + last_stored_ut = st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec; + now_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec; + next_store_ut = (st->last_updated.tv_sec + st->update_every) * USEC_PER_SEC; + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) { + debug(D_RRD_STATS, "%s: last_collect_ut = %0.3Lf (last collection time)", st->name, (long double)last_collect_ut/1000000.0); + debug(D_RRD_STATS, "%s: now_collect_ut = %0.3Lf (current collection time)", st->name, (long double)now_collect_ut/1000000.0); + debug(D_RRD_STATS, "%s: last_stored_ut = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0); + debug(D_RRD_STATS, "%s: next_store_ut = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0); + } + + if(unlikely(!st->counter_done)) { + store_this_entry = 0; + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) + debug(D_RRD_STATS, "%s: Will not store the next entry.", st->name); + } + st->counter_done++; + + if(unlikely(st->rrdhost->rrdpush_enabled)) + rrdset_done_push(st); + + // calculate totals and count the dimensions + int dimensions = 0; + st->collected_total = 0; + rrddim_foreach_read(rd, st) { + 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 + rrddim_foreach_read(rd, st) { + + if(unlikely(!rd->updated)) { + rd->calculated_value = 0; + continue; + } + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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 RRD_ALGORITHM_ABSOLUTE: + rd->calculated_value = (calculated_number)rd->collected_value + * (calculated_number)rd->multiplier + / (calculated_number)rd->divisor; + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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 RRD_ALGORITHM_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(rrdset_flag_check(st, RRDSET_FLAG_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 RRD_ALGORITHM_INCREMENTAL: + if(unlikely(rd->collections_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(!(rrddim_flag_check(rd, 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(rrdset_flag_check(st, RRDSET_FLAG_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 RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL: + if(unlikely(rd->collections_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(!(rrddim_flag_check(rd, 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(rrdset_flag_check(st, RRDSET_FLAG_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(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) + debug(D_RRD_STATS, "%s/%s: CALC " + CALCULATED_NUMBER_FORMAT " = 0" + , st->id, rd->name + , rd->calculated_value + ); + break; + } + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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 + + if(unlikely(now_collect_ut < next_store_ut)) { + // this is collected in the same interpolation point + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) + debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name); + +//#ifdef NETDATA_INTERNAL_CHECKS +// info("%s is collected in the same interpolation point: short by %llu microseconds", st->name, next_store_ut - now_collect_ut); +//#endif + } + + usec_t first_ut = last_stored_ut; + long long iterations = (now_collect_ut - last_stored_ut) / (update_every_ut); + if((now_collect_ut % (update_every_ut)) == 0) iterations++; + + for( ; next_store_ut <= now_collect_ut ; last_collect_ut = next_store_ut, next_store_ut += update_every_ut, iterations-- ) { +//#ifdef NETDATA_INTERNAL_CHECKS +// if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_stored_ut = %llu, next_store_ut = %llu, now_collect_ut = %llu", st->name, first_ut, last_stored_ut, next_store_ut, now_collect_ut); } +//#endif + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) { + debug(D_RRD_STATS, "%s: last_stored_ut = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0); + debug(D_RRD_STATS, "%s: next_store_ut = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0); + } + + st->last_updated.tv_sec = (time_t) (next_store_ut / USEC_PER_SEC); + st->last_updated.tv_usec = 0; + + rrddim_foreach_read(rd, st) { + calculated_number new_value; + + switch(rd->algorithm) { + case RRD_ALGORITHM_INCREMENTAL: + new_value = (calculated_number) + ( rd->calculated_value + * (calculated_number)(next_store_ut - last_collect_ut) + / (calculated_number)(now_collect_ut - last_collect_ut) + ); + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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_store_ut - last_stored_ut) + , (now_collect_ut - last_stored_ut) + ); + + rd->calculated_value -= new_value; + new_value += rd->last_calculated_value; + rd->last_calculated_value = 0; + new_value /= (calculated_number)st->update_every; + + if(unlikely(next_store_ut - last_stored_ut < update_every_ut)) { + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) + debug(D_RRD_STATS, "%s/%s: COLLECTION POINT IS SHORT " CALCULATED_NUMBER_FORMAT " - EXTRAPOLATING", + st->id, rd->name + , (calculated_number)(next_store_ut - last_stored_ut) + ); + new_value = new_value * (calculated_number)(st->update_every * 1000000) / (calculated_number)(next_store_ut - last_stored_ut); + } + break; + + case RRD_ALGORITHM_ABSOLUTE: + case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL: + case RRD_ALGORITHM_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_store_ut - last_collect_ut) + / (calculated_number)(now_collect_ut - last_collect_ut) + ) + + rd->last_calculated_value + ); + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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_store_ut - first_ut) + , (now_collect_ut - first_ut), rd->last_calculated_value + ); + } + break; + } + + if(unlikely(!store_this_entry)) { + rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS); + continue; + } + + if(likely(rd->updated && rd->collections_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(rrdset_flag_check(st, RRDSET_FLAG_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(rrdset_flag_check(st, RRDSET_FLAG_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(rrdset_flag_check(st, RRDSET_FLAG_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_stored_ut = next_store_ut; + } + + st->last_collected_total = st->collected_total; + + rrddim_foreach_read(rd, st) { + if(unlikely(!rd->updated)) + continue; + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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; + + switch(rd->algorithm) { + case RRD_ALGORITHM_INCREMENTAL: + if(unlikely(!first_entry)) { + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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->calculated_value); + rd->last_calculated_value += rd->calculated_value; + } + else { + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) + debug(D_RRD_STATS, "%s: THIS IS THE FIRST POINT", st->name); + } + break; + + case RRD_ALGORITHM_ABSOLUTE: + case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL: + case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL: + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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; + break; + } + + rd->calculated_value = 0; + rd->collected_value = 0; + rd->updated = 0; + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_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)) { + RRDDIM *last; + // there is dimension to free + // upgrade our read lock to a write lock + rrdset_unlock(st); + rrdset_wrlock(st); + + 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; + } + } + } +*/ + + rrdset_unlock(st); + + if(unlikely(pthread_setcancelstate(pthreadoldcancelstate, NULL) != 0)) + error("Cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate); +} diff --git a/src/rrdsetvar.c b/src/rrdsetvar.c new file mode 100644 index 000000000..03d7aeced --- /dev/null +++ b/src/rrdsetvar.c @@ -0,0 +1,120 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDSETVAR management +// CHART VARIABLES + +static inline void rrdsetvar_free_variables(RRDSETVAR *rs) { + RRDSET *st = rs->rrdset; + + // CHART + + rrdvar_free(st->rrdhost, &st->variables_root_index, rs->var_local); + rs->var_local = NULL; + + // FAMILY + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family); + rs->var_family = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host); + rs->var_host = NULL; + + // HOST + + rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->var_family_name); + rs->var_family_name = NULL; + + rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->var_host_name); + rs->var_host_name = NULL; + + // KEYS + + freez(rs->key_fullid); + rs->key_fullid = NULL; + + freez(rs->key_fullname); + rs->key_fullname = NULL; +} + +static inline void rrdsetvar_create_variables(RRDSETVAR *rs) { + rrdsetvar_free_variables(rs); + + RRDSET *st = rs->rrdset; + + // KEYS + + char buffer[RRDVAR_MAX_LENGTH + 1]; + snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rs->variable); + rs->key_fullid = strdupz(buffer); + + snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable); + rs->key_fullname = strdupz(buffer); + + // CHART + + rs->var_local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->type, rs->value); + + // FAMILY + + rs->var_family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullid, rs->type, rs->value); + rs->var_family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->key_fullname, rs->type, rs->value); + + // HOST + + rs->var_host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullid, rs->type, rs->value); + rs->var_host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->key_fullname, rs->type, rs->value); + +} + +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)); + + rs->variable = strdupz(variable); + rs->type = type; + rs->value = value; + rs->options = options; + rs->rrdset = st; + + rs->next = st->variables; + st->variables = rs; + + rrdsetvar_create_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); + + RRDSETVAR *rs, *next = st->variables; + while((rs = next)) { + next = rs->next; + rrdsetvar_create_variables(rs); + } + + 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); + + 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->key_fullname, st->id); + else t->next = rs->next; + } + + rrdsetvar_free_variables(rs); + + freez(rs->variable); + freez(rs); +} + diff --git a/src/rrdvar.c b/src/rrdvar.c new file mode 100644 index 000000000..2223d7c9a --- /dev/null +++ b/src/rrdvar.c @@ -0,0 +1,265 @@ +#define NETDATA_HEALTH_INTERNALS +#include "common.h" + +// ---------------------------------------------------------------------------- +// RRDVAR management + +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); +} + +inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) { + (void)host; + + if(!rv) return; + + if(tree) { + debug(D_VARIABLES, "Deleting variable '%s'", rv->name); + if(unlikely(!rrdvar_index_del(tree, rv))) + error("Attempted to delete variable '%s' from host '%s', but it is not found.", rv->name, host->hostname); + } + + freez(rv->name); + freez(rv); +} + +inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, void *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 { + debug(D_VARIABLES, "Variable '%s' is already found in scope '%s'.", variable, scope); + + // already exists + freez(variable); + + // this is important + // it must return NULL - not the existing variable - or double-free will happen + rv = NULL; + } + + return rv; +} + +// ---------------------------------------------------------------------------- +// CUSTOM VARIABLES + +RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name) { + calculated_number *v = callocz(1, sizeof(calculated_number)); + *v = NAN; + RRDVAR *rv = rrdvar_create_and_index("host", &host->variables_root_index, name, RRDVAR_TYPE_CALCULATED_ALLOCATED, v); + if(unlikely(!rv)) { + free(v); + error("Requested variable '%s' already exists - possibly 2 plugins will be updating it at the same time", name); + + char *variable = strdupz(name); + rrdvar_fix_name(variable); + uint32_t hash = simple_hash(variable); + + rv = rrdvar_index_find(&host->variables_root_index, variable, hash); + } + + return rv; +} + +void rrdvar_custom_host_variable_destroy(RRDHOST *host, const char *name) { + char *variable = strdupz(name); + rrdvar_fix_name(variable); + uint32_t hash = simple_hash(variable); + + RRDVAR *rv = rrdvar_index_find(&host->variables_root_index, variable, hash); + freez(variable); + + if(!rv) { + error("Attempted to remove variable '%s' from host '%s', but it does not exist.", name, host->hostname); + return; + } + + if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) { + error("Attempted to remove variable '%s' from host '%s', but it does not a custom allocated variable.", name, host->hostname); + return; + } + + if(!rrdvar_index_del(&host->variables_root_index, rv)) { + error("Attempted to remove variable '%s' from host '%s', but it cannot be found.", name, host->hostname); + return; + } + + freez(rv->name); + freez(rv->value); + freez(rv); +} + +void rrdvar_custom_host_variable_set(RRDVAR *rv, calculated_number value) { + if(rv->type != RRDVAR_TYPE_CALCULATED_ALLOCATED) + error("requested to set variable '%s' to value " CALCULATED_NUMBER_FORMAT " but the variable is not a custom one.", rv->name, value); + else { + calculated_number *v = rv->value; + *v = value; + } +} + +// ---------------------------------------------------------------------------- +// RRDVAR lookup + +static calculated_number rrdvar2number(RRDVAR *rv) { + switch(rv->type) { + case RRDVAR_TYPE_CALCULATED_ALLOCATED: + 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; + } +} + +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; + } + + return 0; +} + +// ---------------------------------------------------------------------------- +// RRDVAR to JSON + +struct variable2json_helper { + BUFFER *buf; + size_t counter; +}; + +static int single_variable2json(void *entry, void *data) { + struct variable2json_helper *helper = (struct variable2json_helper *)data; + RRDVAR *rv = (RRDVAR *)entry; + calculated_number value = rrdvar2number(rv); + + if(unlikely(isnan(value) || isinf(value))) + buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": null", helper->counter?",":"", rv->name); + else + buffer_sprintf(helper->buf, "%s\n\t\t\"%s\": %0.5Lf", helper->counter?",":"", rv->name, (long double)value); + + helper->counter++; + + return 0; +} + +void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) { + struct variable2json_helper helper = { + .buf = buf, + .counter = 0 + }; + + buffer_sprintf(buf, "{\n\t\"chart\": \"%s\",\n\t\"chart_name\": \"%s\",\n\t\"chart_context\": \"%s\",\n\t\"chart_variables\": {", st->id, st->name, st->context); + avl_traverse_lock(&st->variables_root_index, single_variable2json, (void *)&helper); + buffer_sprintf(buf, "\n\t},\n\t\"family\": \"%s\",\n\t\"family_variables\": {", st->family); + helper.counter = 0; + avl_traverse_lock(&st->rrdfamily->variables_root_index, single_variable2json, (void *)&helper); + buffer_sprintf(buf, "\n\t},\n\t\"host\": \"%s\",\n\t\"host_variables\": {", st->rrdhost->hostname); + helper.counter = 0; + avl_traverse_lock(&st->rrdhost->variables_root_index, single_variable2json, (void *)&helper); + buffer_strcat(buf, "\n\t}\n}\n"); +} + diff --git a/src/simple_pattern.c b/src/simple_pattern.c index 7e4424297..f72a42d06 100644 --- a/src/simple_pattern.c +++ b/src/simple_pattern.c @@ -169,7 +169,7 @@ static inline int match_pattern(struct simple_pattern *m, const char *str, size_ int simple_pattern_matches(SIMPLE_PATTERN *list, const char *str) { struct simple_pattern *m, *root = (struct simple_pattern *)list; - if(unlikely(!root)) return 0; + if(unlikely(!root || !str || !*str)) return 0; size_t len = strlen(str); for(m = root; m ; m = m->next) @@ -184,8 +184,8 @@ int simple_pattern_matches(SIMPLE_PATTERN *list, const char *str) { static inline void free_pattern(struct simple_pattern *m) { if(!m) return; - if(m->next) free_pattern(m->next); - if(m->child) free_pattern(m->child); + free_pattern(m->child); + free_pattern(m->next); freez((void *)m->match); freez(m); } @@ -193,5 +193,5 @@ static inline void free_pattern(struct simple_pattern *m) { void simple_pattern_free(SIMPLE_PATTERN *list) { if(!list) return; - free_pattern(((struct simple_pattern *)list)->next); + free_pattern(((struct simple_pattern *)list)); } diff --git a/src/socket.c b/src/socket.c index 643811e44..400c1ef4e 100644 --- a/src/socket.c +++ b/src/socket.c @@ -160,8 +160,10 @@ int connect_to(const char *definition, int default_port, struct timeval *timeout fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if(fd != -1) { - if(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)timeout, sizeof(struct timeval)) < 0) - error("Failed to set timeout on the socket to ip '%s' port '%s'", hostBfr, servBfr); + if(timeout) { + if(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *) timeout, sizeof(struct timeval)) < 0) + error("Failed to set timeout on the socket to ip '%s' port '%s'", hostBfr, servBfr); + } if(connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) { error("Failed to connect to '%s', port '%s'", hostBfr, servBfr); @@ -177,3 +179,98 @@ int connect_to(const char *definition, int default_port, struct timeval *timeout return fd; } + +int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) { + int sock = -1; + + const char *s = destination; + while(*s) { + const 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); + if(reconnects_counter) *reconnects_counter += 1; + sock = connect_to(buf, default_port, timeout); + if(sock != -1) { + if(connected_to && connected_to_size) { + strncpy(connected_to, buf, connected_to_size); + connected_to[connected_to_size - 1] = '\0'; + } + break; + } + s = e; + } + + return sock; +} + +ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) { + for(;;) { + struct pollfd fd = { + .fd = sockfd, + .events = POLLIN, + .revents = 0 + }; + + errno = 0; + int retval = poll(&fd, 1, timeout * 1000); + + if(retval == -1) { + // failed + + if(errno == EINTR || errno == EAGAIN) + continue; + + return -1; + } + + if(!retval) { + // timeout + return 0; + } + + if(fd.events & POLLIN) break; + } + + return recv(sockfd, buf, len, flags); +} + +ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) { + for(;;) { + struct pollfd fd = { + .fd = sockfd, + .events = POLLOUT, + .revents = 0 + }; + + errno = 0; + int retval = poll(&fd, 1, timeout * 1000); + + if(retval == -1) { + // failed + + if(errno == EINTR || errno == EAGAIN) + continue; + + return -1; + } + + if(!retval) { + // timeout + return 0; + } + + if(fd.events & POLLOUT) break; + } + + return send(sockfd, buf, len, flags); +} diff --git a/src/socket.h b/src/socket.h index 791c0ce54..89c154a61 100644 --- a/src/socket.h +++ b/src/socket.h @@ -6,5 +6,9 @@ #define NETDATA_SOCKET_H extern int connect_to(const char *definition, int default_port, struct timeval *timeout); +extern int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size); + +extern ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); +extern ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); #endif //NETDATA_SOCKET_H diff --git a/src/sys_devices_system_edac_mc.c b/src/sys_devices_system_edac_mc.c index c764615f1..c41ad7faa 100644 --- a/src/sys_devices_system_edac_mc.c +++ b/src/sys_devices_system_edac_mc.c @@ -23,7 +23,7 @@ static struct mc *mc_root = NULL; static void find_all_mc() { char name[FILENAME_MAX + 1]; - snprintfz(name, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/devices/system/edac/mc"); + snprintfz(name, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/edac/mc"); char *dirname = config_get("plugin:proc:/sys/devices/system/edac/mc", "directory to monitor", name); DIR *dir = opendir(dirname); @@ -76,11 +76,11 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) { struct mc *m; if(unlikely(do_ce == -1)) { - do_ce = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory correctable errors", CONFIG_ONDEMAND_ONDEMAND); - do_ue = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory uncorrectable errors", CONFIG_ONDEMAND_ONDEMAND); + do_ce = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory correctable errors", CONFIG_BOOLEAN_AUTO); + do_ue = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/edac/mc", "enable ECC memory uncorrectable errors", CONFIG_BOOLEAN_AUTO); } - if(do_ce != CONFIG_ONDEMAND_NO) { + if(do_ce != CONFIG_BOOLEAN_NO) { for(m = mc_root; m; m = m->next) { if(m->ce_count_filename) { m->ce_updated = 0; @@ -102,7 +102,7 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) { } } - if(do_ue != CONFIG_ONDEMAND_NO) { + if(do_ue != CONFIG_BOOLEAN_NO) { for(m = mc_root; m; m = m->next) { if(m->ue_count_filename) { m->ue_updated = 0; @@ -126,20 +126,20 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_ce == CONFIG_ONDEMAND_YES || (do_ce == CONFIG_ONDEMAND_ONDEMAND && ce_sum > 0)) { - do_ce = CONFIG_ONDEMAND_YES; + if(do_ce == CONFIG_BOOLEAN_YES || (do_ce == CONFIG_BOOLEAN_AUTO && ce_sum > 0)) { + do_ce = CONFIG_BOOLEAN_YES; static RRDSET *ce_st = NULL; if(unlikely(!ce_st)) { - ce_st = rrdset_find("mem.ecc_ce"); + ce_st = rrdset_find_localhost("mem.ecc_ce"); if(unlikely(!ce_st)) - ce_st = rrdset_create("mem", "ecc_ce", NULL, "ecc", NULL, "ECC Memory Correctable Errors", "errors", - 6600, update_every, RRDSET_TYPE_LINE); + ce_st = rrdset_create_localhost("mem", "ecc_ce", NULL, "ecc", NULL, "ECC Memory Correctable Errors" + , "errors", 6600, update_every, RRDSET_TYPE_LINE); for(m = mc_root; m; m = m->next) if(m->ce_count_filename) - m->ce_rd = rrddim_add(ce_st, m->name, NULL, 1, 1, RRDDIM_INCREMENTAL); + m->ce_rd = rrddim_add(ce_st, m->name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(ce_st); @@ -153,21 +153,21 @@ int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt) { // -------------------------------------------------------------------- - if(do_ue == CONFIG_ONDEMAND_YES || (do_ue == CONFIG_ONDEMAND_ONDEMAND && ue_sum > 0)) { - do_ue = CONFIG_ONDEMAND_YES; + if(do_ue == CONFIG_BOOLEAN_YES || (do_ue == CONFIG_BOOLEAN_AUTO && ue_sum > 0)) { + do_ue = CONFIG_BOOLEAN_YES; static RRDSET *ue_st = NULL; if(unlikely(!ue_st)) { - ue_st = rrdset_find("mem.ecc_ue"); + ue_st = rrdset_find_localhost("mem.ecc_ue"); if(unlikely(!ue_st)) - ue_st = rrdset_create("mem", "ecc_ue", NULL, "ecc", NULL, "ECC Memory Uncorrectable Errors", "errors", - 6610, update_every, RRDSET_TYPE_LINE); + ue_st = rrdset_create_localhost("mem", "ecc_ue", NULL, "ecc", NULL, "ECC Memory Uncorrectable Errors" + , "errors", 6610, update_every, RRDSET_TYPE_LINE); for(m = mc_root; m; m = m->next) if(m->ue_count_filename) - m->ue_rd = rrddim_add(ue_st, m->name, NULL, 1, 1, RRDDIM_INCREMENTAL); + m->ue_rd = rrddim_add(ue_st, m->name, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(ue_st); diff --git a/src/sys_devices_system_node.c b/src/sys_devices_system_node.c index 18c3fcd3a..a7690e7b7 100644 --- a/src/sys_devices_system_node.c +++ b/src/sys_devices_system_node.c @@ -12,7 +12,7 @@ static struct node *numa_root = NULL; static int find_all_nodes() { int numa_node_count = 0; char name[FILENAME_MAX + 1]; - snprintfz(name, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/devices/system/node"); + snprintfz(name, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/node"); char *dirname = config_get("plugin:proc:/sys/devices/system/node", "directory to monitor", name); DIR *dir = opendir(dirname); @@ -60,26 +60,34 @@ static int find_all_nodes() { int do_proc_sys_devices_system_node(int update_every, usec_t dt) { (void)dt; - static int numa_node_count = 0; + static uint32_t hash_local_node = 0, hash_numa_foreign = 0, hash_interleave_hit = 0, hash_other_node = 0, hash_numa_hit = 0, hash_numa_miss = 0; + static int do_numastat = -1, numa_node_count = 0; + struct node *m; if(unlikely(numa_root == NULL)) { - numa_node_count = find_all_nodes(update_every); + numa_node_count = find_all_nodes(); if(unlikely(numa_root == NULL)) return 1; } - static int do_numastat = -1; - struct node *m; - if(unlikely(do_numastat == -1)) { - do_numastat = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/node", "enable per-node numa metrics", CONFIG_ONDEMAND_ONDEMAND); + do_numastat = config_get_boolean_ondemand("plugin:proc:/sys/devices/system/node", "enable per-node numa metrics", CONFIG_BOOLEAN_AUTO); + + hash_local_node = simple_hash("local_node"); + hash_numa_foreign = simple_hash("numa_foreign"); + hash_interleave_hit = simple_hash("interleave_hit"); + hash_other_node = simple_hash("other_node"); + hash_numa_hit = simple_hash("numa_hit"); + hash_numa_miss = simple_hash("numa_miss"); } - if(do_numastat == CONFIG_ONDEMAND_YES || (do_numastat == CONFIG_ONDEMAND_ONDEMAND && numa_node_count >= 2)) { + if(do_numastat == CONFIG_BOOLEAN_YES || (do_numastat == CONFIG_BOOLEAN_AUTO && numa_node_count >= 2)) { for(m = numa_root; m; m = m->next) { if(m->numastat_filename) { + if(unlikely(!m->numastat_ff)) { m->numastat_ff = procfile_open(m->numastat_filename, " ", PROCFILE_FLAG_DEFAULT); + if(unlikely(!m->numastat_ff)) continue; } @@ -88,37 +96,61 @@ int do_proc_sys_devices_system_node(int update_every, usec_t dt) { if(unlikely(!m->numastat_ff || procfile_lines(m->numastat_ff) < 1 || procfile_linewords(m->numastat_ff, 0) < 1)) continue; - procfile *ff = m->numastat_ff; - - RRDSET *st = m->numastat_st; - if(unlikely(!st)) { - st = rrdset_create("mem", m->name, NULL, "numa", NULL, "NUMA events", "events/s", 1000, update_every, RRDSET_TYPE_LINE); - st->isdetail = 1; + if(unlikely(!m->numastat_st)) { + m->numastat_st = rrdset_create_localhost( + "mem" + , m->name + , NULL + , "numa" + , NULL + , "NUMA events" + , "events/s" + , 1000 + , update_every + , RRDSET_TYPE_LINE + ); + + rrdset_flag_set(m->numastat_st, RRDSET_FLAG_DETAIL); + + rrddim_add(m->numastat_st, "numa_hit", "hit", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(m->numastat_st, "numa_miss", "miss", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(m->numastat_st, "local_node", "local", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(m->numastat_st, "numa_foreign", "foreign", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(m->numastat_st, "interleave_hit", "interleave", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(m->numastat_st, "other_node", "other", 1, 1, RRD_ALGORITHM_INCREMENTAL); - rrddim_add(st, "local_node", "local", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "numa_foreign", "foreign", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "interleave_hit", "interleave", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "other_node", "other", 1, 1, RRDDIM_INCREMENTAL); - - m->numastat_st = st; } - else rrdset_next(st); + else rrdset_next(m->numastat_st); - uint32_t lines = procfile_lines(ff), l; + size_t lines = procfile_lines(m->numastat_ff), l; for(l = 0; l < lines; l++) { - uint32_t words = procfile_linewords(ff, l); + size_t words = procfile_linewords(m->numastat_ff, l); + if(unlikely(words < 2)) { - if(unlikely(words)) error("Cannot read %s numastat line %u. Expected 2 params, read %u.", m->name, l, words); + if(unlikely(words)) + error("Cannot read %s numastat line %zu. Expected 2 params, read %zu.", m->name, l, words); continue; } - char *name = procfile_lineword(ff, l, 0); - char *value = procfile_lineword(ff, l, 1); - if (unlikely(!name || !*name || !value || !*value)) continue; + char *name = procfile_lineword(m->numastat_ff, l, 0); + char *value = procfile_lineword(m->numastat_ff, l, 1); - rrddim_set(st, name, strtoull(value, NULL, 10)); + if (unlikely(!name || !*name || !value || !*value)) + continue; + + uint32_t hash = simple_hash(name); + if(likely( + (hash == hash_numa_hit && !strcmp(name, "numa_hit")) + || (hash == hash_numa_miss && !strcmp(name, "numa_miss")) + || (hash == hash_local_node && !strcmp(name, "local_node")) + || (hash == hash_numa_foreign && !strcmp(name, "numa_foreign")) + || (hash == hash_interleave_hit && !strcmp(name, "interleave_hit")) + || (hash == hash_other_node && !strcmp(name, "other_node")) + )) + rrddim_set(m->numastat_st, name, (collected_number)str2kernel_uint_t(value)); } - rrdset_done(st); + + rrdset_done(m->numastat_st); } } } diff --git a/src/sys_fs_cgroup.c b/src/sys_fs_cgroup.c index 2b7254f47..8f31527de 100644 --- a/src/sys_fs_cgroup.c +++ b/src/sys_fs_cgroup.c @@ -8,22 +8,22 @@ static long system_page_size = 4096; // system will be queried via sysconf() in configuration() -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_detailed_memory = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_memory_failcnt = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_swap = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_blkio_io = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_blkio_ops = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_blkio_throttle_io = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_blkio_throttle_ops = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_blkio_merged_ops = CONFIG_ONDEMAND_ONDEMAND; -static int cgroup_enable_blkio_queued_ops = CONFIG_ONDEMAND_ONDEMAND; - -static int cgroup_enable_systemd_services = CONFIG_ONDEMAND_YES; -static int cgroup_enable_systemd_services_detailed_memory = CONFIG_ONDEMAND_NO; -static int cgroup_used_memory_without_cache = CONFIG_ONDEMAND_YES; +static int cgroup_enable_cpuacct_stat = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_cpuacct_usage = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_memory = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_detailed_memory = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_memory_failcnt = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_swap = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_blkio_io = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_blkio_ops = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_blkio_throttle_io = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_blkio_throttle_ops = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_blkio_merged_ops = CONFIG_BOOLEAN_AUTO; +static int cgroup_enable_blkio_queued_ops = CONFIG_BOOLEAN_AUTO; + +static int cgroup_enable_systemd_services = CONFIG_BOOLEAN_YES; +static int cgroup_enable_systemd_services_detailed_memory = CONFIG_BOOLEAN_NO; +static int cgroup_used_memory_without_cache = CONFIG_BOOLEAN_YES; static int cgroup_search_in_devices = 1; @@ -49,7 +49,9 @@ static SIMPLE_PATTERN *enabled_cgroup_paths = NULL; static SIMPLE_PATTERN *enabled_cgroup_renames = NULL; static SIMPLE_PATTERN *systemd_services_cgroups = NULL; -static char *cgroups_rename_script = PLUGINS_DIR "/cgroup-name.sh"; +static char *cgroups_rename_script = NULL; + +static int cgroups_check = 0; static uint32_t Read_hash = 0; static uint32_t Write_hash = 0; @@ -64,9 +66,9 @@ void read_cgroup_plugin_configuration() { user_hash = simple_hash("user"); system_hash = simple_hash("system"); - cgroup_update_every = (int)config_get_number("plugin:cgroups", "update every", rrd_update_every); - if(cgroup_update_every < rrd_update_every) - cgroup_update_every = rrd_update_every; + cgroup_update_every = (int)config_get_number("plugin:cgroups", "update every", localhost->rrd_update_every); + if(cgroup_update_every < localhost->rrd_update_every) + cgroup_update_every = localhost->rrd_update_every; cgroup_check_for_new_every = (int)config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every * cgroup_update_every); if(cgroup_check_for_new_every < cgroup_update_every) @@ -105,7 +107,7 @@ void read_cgroup_plugin_configuration() { s = "/sys/fs/cgroup/cpuacct"; } else s = mi->mount_point; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_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"); @@ -115,7 +117,7 @@ void read_cgroup_plugin_configuration() { s = "/sys/fs/cgroup/blkio"; } else s = mi->mount_point; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_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"); @@ -125,7 +127,7 @@ void read_cgroup_plugin_configuration() { s = "/sys/fs/cgroup/memory"; } else s = mi->mount_point; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_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"); @@ -135,7 +137,7 @@ void read_cgroup_plugin_configuration() { s = "/sys/fs/cgroup/devices"; } else s = mi->mount_point; - snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s); + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, s); cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename); cgroup_root_max = (int)config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max); @@ -145,6 +147,8 @@ void read_cgroup_plugin_configuration() { enabled_cgroup_patterns = simple_pattern_create( config_get("plugin:cgroups", "enable by default cgroups matching", + " /system.slice/docker-*.scope " + " /qemu.slice/*.scope " // #1949 " !*.mount " " !*.partition " " !*.scope " @@ -156,18 +160,18 @@ void read_cgroup_plugin_configuration() { " !/docker " " !/libvirt " " !/lxc " - " !/lxc/*/ns " // #1397 + " !/lxc/*/ns " // #1397 " !/machine " " !/qemu " " !/system " " !/systemd " " !/user " - " * " // enable anything else + " * " // enable anything else ), SIMPLE_PATTERN_EXACT); enabled_cgroup_paths = simple_pattern_create( config_get("plugin:cgroups", "search for cgroups in subpaths matching", - " !*-qemu " // #345 + " !*-qemu " // #345 " !/init.scope " " !/system " " !/systemd " @@ -176,10 +180,14 @@ void read_cgroup_plugin_configuration() { " * " ), SIMPLE_PATTERN_EXACT); - cgroups_rename_script = config_get("plugin:cgroups", "script to get cgroup names", cgroups_rename_script); + snprintfz(filename, FILENAME_MAX, "%s/cgroup-name.sh", netdata_configured_plugins_dir); + cgroups_rename_script = config_get("plugin:cgroups", "script to get cgroup names", filename); enabled_cgroup_renames = simple_pattern_create( config_get("plugin:cgroups", "run script to rename cgroups matching", + " /qemu.slice/*.scope " // #1949 + " *docker* " + " *lxc* " " !/ " " !*.mount " " !*.partition " @@ -207,7 +215,7 @@ void read_cgroup_plugin_configuration() { struct blkio { int updated; - int enabled; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND + int enabled; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO int delay_counter; char *filename; @@ -232,10 +240,10 @@ struct memory { int updated_msw_usage_in_bytes; int updated_failcnt; - int enabled_detailed; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND - int enabled_usage_in_bytes; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND - int enabled_msw_usage_in_bytes; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND - int enabled_failcnt; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND + int enabled_detailed; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO + int enabled_usage_in_bytes; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO + int enabled_msw_usage_in_bytes; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO + int enabled_failcnt; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO int delay_counter_detailed; int delay_counter_failcnt; @@ -294,7 +302,7 @@ struct memory { // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt struct cpuacct_stat { int updated; - int enabled; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND + int enabled; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO char *filename; @@ -305,7 +313,7 @@ struct cpuacct_stat { // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt struct cpuacct_usage { int updated; - int enabled; // CONFIG_ONDEMAND_YES or CONFIG_ONDEMAND_ONDEMAND + int enabled; // CONFIG_BOOLEAN_YES or CONFIG_BOOLEAN_AUTO char *filename; @@ -403,12 +411,14 @@ static inline void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) { ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) { cp->updated = 0; + cgroups_check = 1; return; } ff = procfile_readall(ff); if(unlikely(!ff)) { cp->updated = 0; + cgroups_check = 1; return; } @@ -433,8 +443,8 @@ static inline void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) { cp->updated = 1; - if(unlikely(cp->enabled == CONFIG_ONDEMAND_ONDEMAND && (cp->user || cp->system))) - cp->enabled = CONFIG_ONDEMAND_YES; + if(unlikely(cp->enabled == CONFIG_BOOLEAN_AUTO && (cp->user || cp->system))) + cp->enabled = CONFIG_BOOLEAN_YES; } } @@ -445,12 +455,14 @@ static inline void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) { ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) { ca->updated = 0; + cgroups_check = 1; return; } ff = procfile_readall(ff); if(unlikely(!ff)) { ca->updated = 0; + cgroups_check = 1; return; } @@ -462,8 +474,8 @@ static inline void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) { unsigned long i = procfile_linewords(ff, 0); if(unlikely(i == 0)) { - return; ca->updated = 0; + return; } // we may have 1 more CPU reported @@ -488,15 +500,15 @@ static inline void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) { ca->updated = 1; - if(unlikely(ca->enabled == CONFIG_ONDEMAND_ONDEMAND && total)) - ca->enabled = CONFIG_ONDEMAND_YES; + if(unlikely(ca->enabled == CONFIG_BOOLEAN_AUTO && total)) + ca->enabled = CONFIG_BOOLEAN_YES; } } static inline void cgroup_read_blkio(struct blkio *io) { static procfile *ff = NULL; - if(unlikely(io->enabled == CONFIG_ONDEMAND_ONDEMAND && io->delay_counter > 0)) { + if(unlikely(io->enabled == CONFIG_BOOLEAN_AUTO && io->delay_counter > 0)) { io->delay_counter--; return; } @@ -505,12 +517,14 @@ static inline void cgroup_read_blkio(struct blkio *io) { ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) { io->updated = 0; + cgroups_check = 1; return; } ff = procfile_readall(ff); if(unlikely(!ff)) { io->updated = 0; + cgroups_check = 1; return; } @@ -554,9 +568,9 @@ static inline void cgroup_read_blkio(struct blkio *io) { io->updated = 1; - if(unlikely(io->enabled == CONFIG_ONDEMAND_ONDEMAND)) { + if(unlikely(io->enabled == CONFIG_BOOLEAN_AUTO)) { if(unlikely(io->Read || io->Write)) - io->enabled = CONFIG_ONDEMAND_YES; + io->enabled = CONFIG_BOOLEAN_YES; else io->delay_counter = cgroup_recheck_zero_blkio_every_iterations; } @@ -568,7 +582,7 @@ static inline void cgroup_read_memory(struct memory *mem) { // read detailed ram usage if(likely(mem->filename_detailed)) { - if(unlikely(mem->enabled_detailed == CONFIG_ONDEMAND_ONDEMAND && mem->delay_counter_detailed > 0)) { + if(unlikely(mem->enabled_detailed == CONFIG_BOOLEAN_AUTO && mem->delay_counter_detailed > 0)) { mem->delay_counter_detailed--; goto memory_next; } @@ -576,12 +590,14 @@ static inline void cgroup_read_memory(struct memory *mem) { ff = procfile_reopen(ff, mem->filename_detailed, NULL, PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) { mem->updated_detailed = 0; + cgroups_check = 1; goto memory_next; } ff = procfile_readall(ff); if(unlikely(!ff)) { mem->updated_detailed = 0; + cgroups_check = 1; goto memory_next; } @@ -627,9 +643,9 @@ static inline void cgroup_read_memory(struct memory *mem) { mem->updated_detailed = 1; - if(unlikely(mem->enabled_detailed == CONFIG_ONDEMAND_ONDEMAND)) { + if(unlikely(mem->enabled_detailed == CONFIG_BOOLEAN_AUTO)) { if(mem->cache || mem->dirty || mem->rss || mem->rss_huge || mem->mapped_file || mem->writeback || mem->swap || mem->pgpgin || mem->pgpgout || mem->pgfault || mem->pgmajfault) - mem->enabled_detailed = CONFIG_ONDEMAND_YES; + mem->enabled_detailed = CONFIG_BOOLEAN_YES; else mem->delay_counter_detailed = cgroup_recheck_zero_mem_detailed_every_iterations; } @@ -640,30 +656,30 @@ memory_next: // read usage_in_bytes if(likely(mem->filename_usage_in_bytes)) { mem->updated_usage_in_bytes = !read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes); - if(unlikely(mem->updated_usage_in_bytes && mem->enabled_usage_in_bytes == CONFIG_ONDEMAND_ONDEMAND && mem->usage_in_bytes)) - mem->enabled_usage_in_bytes = CONFIG_ONDEMAND_YES; + if(unlikely(mem->updated_usage_in_bytes && mem->enabled_usage_in_bytes == CONFIG_BOOLEAN_AUTO && mem->usage_in_bytes)) + mem->enabled_usage_in_bytes = CONFIG_BOOLEAN_YES; } // read msw_usage_in_bytes if(likely(mem->filename_msw_usage_in_bytes)) { mem->updated_msw_usage_in_bytes = !read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes); - if(unlikely(mem->updated_msw_usage_in_bytes && mem->enabled_msw_usage_in_bytes == CONFIG_ONDEMAND_ONDEMAND && mem->msw_usage_in_bytes)) - mem->enabled_msw_usage_in_bytes = CONFIG_ONDEMAND_YES; + if(unlikely(mem->updated_msw_usage_in_bytes && mem->enabled_msw_usage_in_bytes == CONFIG_BOOLEAN_AUTO && mem->msw_usage_in_bytes)) + mem->enabled_msw_usage_in_bytes = CONFIG_BOOLEAN_YES; } // read failcnt if(likely(mem->filename_failcnt)) { - if(unlikely(mem->enabled_failcnt == CONFIG_ONDEMAND_ONDEMAND && mem->delay_counter_failcnt > 0)) { + if(unlikely(mem->enabled_failcnt == CONFIG_BOOLEAN_AUTO && mem->delay_counter_failcnt > 0)) { mem->updated_failcnt = 0; mem->delay_counter_failcnt--; } else { mem->updated_failcnt = !read_single_number_file(mem->filename_failcnt, &mem->failcnt); - if(unlikely(mem->updated_failcnt && mem->enabled_failcnt == CONFIG_ONDEMAND_ONDEMAND)) { + if(unlikely(mem->updated_failcnt && mem->enabled_failcnt == CONFIG_BOOLEAN_AUTO)) { if(unlikely(!mem->failcnt)) mem->delay_counter_failcnt = cgroup_recheck_zero_mem_failcnt_every_iterations; else - mem->enabled_failcnt = CONFIG_ONDEMAND_YES; + mem->enabled_failcnt = CONFIG_BOOLEAN_YES; } } } @@ -877,6 +893,21 @@ static inline struct cgroup *cgroup_add(const char *id) { static inline 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"); + if(cg->st_cpu) rrdset_flag_set(cg->st_cpu, RRDSET_FLAG_OBSOLETE); + if(cg->st_cpu_per_core) rrdset_flag_set(cg->st_cpu_per_core, RRDSET_FLAG_OBSOLETE); + if(cg->st_mem) rrdset_flag_set(cg->st_mem, RRDSET_FLAG_OBSOLETE); + if(cg->st_writeback) rrdset_flag_set(cg->st_writeback, RRDSET_FLAG_OBSOLETE); + if(cg->st_mem_activity) rrdset_flag_set(cg->st_mem_activity, RRDSET_FLAG_OBSOLETE); + if(cg->st_pgfaults) rrdset_flag_set(cg->st_pgfaults, RRDSET_FLAG_OBSOLETE); + if(cg->st_mem_usage) rrdset_flag_set(cg->st_mem_usage, RRDSET_FLAG_OBSOLETE); + if(cg->st_mem_failcnt) rrdset_flag_set(cg->st_mem_failcnt, RRDSET_FLAG_OBSOLETE); + if(cg->st_io) rrdset_flag_set(cg->st_io, RRDSET_FLAG_OBSOLETE); + if(cg->st_serviced_ops) rrdset_flag_set(cg->st_serviced_ops, RRDSET_FLAG_OBSOLETE); + if(cg->st_throttle_io) rrdset_flag_set(cg->st_throttle_io, RRDSET_FLAG_OBSOLETE); + if(cg->st_throttle_serviced_ops) rrdset_flag_set(cg->st_throttle_serviced_ops, RRDSET_FLAG_OBSOLETE); + if(cg->st_queued_ops) rrdset_flag_set(cg->st_queued_ops, RRDSET_FLAG_OBSOLETE); + if(cg->st_merged_ops) rrdset_flag_set(cg->st_merged_ops, RRDSET_FLAG_OBSOLETE); + freez(cg->cpuacct_usage.cpu_percpu); freez(cg->cpuacct_stat.filename); @@ -1070,7 +1101,7 @@ static inline void find_all_cgroups() { 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 = CONFIG_ONDEMAND_NO; + cgroup_enable_cpuacct_usage = CONFIG_BOOLEAN_NO; error("disabled CGROUP cpu statistics."); } } @@ -1082,7 +1113,7 @@ static inline void find_all_cgroups() { cgroup_enable_blkio_throttle_io = cgroup_enable_blkio_throttle_ops = cgroup_enable_blkio_merged_ops = - cgroup_enable_blkio_queued_ops = CONFIG_ONDEMAND_NO; + cgroup_enable_blkio_queued_ops = CONFIG_BOOLEAN_NO; error("disabled CGROUP blkio statistics."); } } @@ -1092,7 +1123,7 @@ static inline void find_all_cgroups() { cgroup_enable_memory = cgroup_enable_detailed_memory = cgroup_enable_swap = - cgroup_enable_memory_failcnt = CONFIG_ONDEMAND_NO; + cgroup_enable_memory_failcnt = CONFIG_BOOLEAN_NO; error("disabled CGROUP memory statistics."); } } @@ -1146,7 +1177,7 @@ static inline void find_all_cgroups() { snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id); if(likely(stat(filename, &buf) != -1)) { cg->memory.filename_detailed = strdupz(filename); - cg->memory.enabled_detailed = (cgroup_enable_detailed_memory == CONFIG_ONDEMAND_YES)?CONFIG_ONDEMAND_YES:CONFIG_ONDEMAND_ONDEMAND; + cg->memory.enabled_detailed = (cgroup_enable_detailed_memory == CONFIG_BOOLEAN_YES)?CONFIG_BOOLEAN_YES:CONFIG_BOOLEAN_AUTO; debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_detailed); } else @@ -1262,18 +1293,19 @@ static inline void find_all_cgroups() { #define CHART_TITLE_MAX 300 -void update_services_charts(int update_every, - int do_cpu, - int do_mem_usage, - int do_mem_detailed, - int do_mem_failcnt, - int do_swap_usage, - int do_io, - int do_io_ops, - int do_throttle_io, - int do_throttle_ops, - int do_queued_ops, - int do_merged_ops +void update_systemd_services_charts( + int update_every + , int do_cpu + , int do_mem_usage + , int do_mem_detailed + , int do_mem_failcnt + , int do_swap_usage + , int do_io + , int do_io_ops + , int do_throttle_io + , int do_throttle_ops + , int do_queued_ops + , int do_merged_ops ) { static RRDSET *st_cpu = NULL, @@ -1309,12 +1341,21 @@ void update_services_charts(int update_every, if(likely(do_cpu)) { if(unlikely(!st_cpu)) { char title[CHART_TITLE_MAX + 1]; + snprintfz(title, CHART_TITLE_MAX, "Systemd Services CPU utilization (%d%% = %d core%s)", (processors * 100), processors, (processors > 1) ? "s" : ""); + + st_cpu = rrdset_create_localhost( + "services" + , "cpu" + , NULL + , "cpu" + , "services.cpu" + , title + , "%" + , CHART_PRIORITY_SYSTEMD_SERVICES + , update_every + , RRDSET_TYPE_STACKED + ); - st_cpu = rrdset_find_bytype("services", "cpu"); - if(likely(!st_cpu)) { - snprintfz(title, CHART_TITLE_MAX, "Systemd Services CPU utilization (%d%% = %d core%s)", (processors * 100), processors, (processors > 1) ? "s" : ""); - st_cpu = rrdset_create("services", "cpu", NULL, "cpu", "services.cpu", title, "%", CHART_PRIORITY_SYSTEMD_SERVICES, update_every, RRDSET_TYPE_STACKED); - } } else rrdset_next(st_cpu); @@ -1322,9 +1363,21 @@ void update_services_charts(int update_every, if(likely(do_mem_usage)) { if(unlikely(!st_mem_usage)) { - st_mem_usage = rrdset_find_bytype("services", "mem_usage"); - if(likely(!st_mem_usage)) - st_mem_usage = rrdset_create("services", "mem_usage", NULL, "mem", "services.mem_usage", (cgroup_used_memory_without_cache)?"Systemd Services Used Memory without Cache":"Systemd Services Used Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 10, update_every, RRDSET_TYPE_STACKED); + + st_mem_usage = rrdset_create_localhost( + "services" + , "mem_usage" + , NULL + , "mem" + , "services.mem_usage" + , (cgroup_used_memory_without_cache) ? "Systemd Services Used Memory without Cache" + : "Systemd Services Used Memory" + , "MB" + , CHART_PRIORITY_SYSTEMD_SERVICES + 10 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_mem_usage); @@ -1332,65 +1385,152 @@ void update_services_charts(int update_every, if(likely(do_mem_detailed)) { if(unlikely(!st_mem_detailed_rss)) { - st_mem_detailed_rss = rrdset_find_bytype("services", "mem_rss"); - if(likely(!st_mem_detailed_rss)) - st_mem_detailed_rss = rrdset_create("services", "mem_rss", NULL, "mem", "services.mem_rss", "Systemd Services RSS Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 20, update_every, RRDSET_TYPE_STACKED); + + st_mem_detailed_rss = rrdset_create_localhost( + "services" + , "mem_rss" + , NULL + , "mem" + , "services.mem_rss" + , "Systemd Services RSS Memory" + , "MB" + , CHART_PRIORITY_SYSTEMD_SERVICES + 20 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_mem_detailed_rss); if(unlikely(!st_mem_detailed_mapped)) { - st_mem_detailed_mapped = rrdset_find_bytype("services", "mem_mapped"); - if(likely(!st_mem_detailed_mapped)) - st_mem_detailed_mapped = rrdset_create("services", "mem_mapped", NULL, "mem", "services.mem_mapped", "Systemd Services Mapped Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 30, update_every, RRDSET_TYPE_STACKED); + + st_mem_detailed_mapped = rrdset_create_localhost( + "services" + , "mem_mapped" + , NULL + , "mem" + , "services.mem_mapped" + , "Systemd Services Mapped Memory" + , "MB" + , CHART_PRIORITY_SYSTEMD_SERVICES + 30 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_mem_detailed_mapped); if(unlikely(!st_mem_detailed_cache)) { - st_mem_detailed_cache = rrdset_find_bytype("services", "mem_cache"); - if(likely(!st_mem_detailed_cache)) - st_mem_detailed_cache = rrdset_create("services", "mem_cache", NULL, "mem", "services.mem_cache", "Systemd Services Cache Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 40, update_every, RRDSET_TYPE_STACKED); + + st_mem_detailed_cache = rrdset_create_localhost( + "services" + , "mem_cache" + , NULL + , "mem" + , "services.mem_cache" + , "Systemd Services Cache Memory" + , "MB" + , CHART_PRIORITY_SYSTEMD_SERVICES + 40 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_mem_detailed_cache); if(unlikely(!st_mem_detailed_writeback)) { - st_mem_detailed_writeback = rrdset_find_bytype("services", "mem_writeback"); - if(likely(!st_mem_detailed_writeback)) - st_mem_detailed_writeback = rrdset_create("services", "mem_writeback", NULL, "mem", "services.mem_writeback", "Systemd Services Writeback Memory", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 50, update_every, RRDSET_TYPE_STACKED); + + st_mem_detailed_writeback = rrdset_create_localhost( + "services" + , "mem_writeback" + , NULL + , "mem" + , "services.mem_writeback" + , "Systemd Services Writeback Memory" + , "MB" + , CHART_PRIORITY_SYSTEMD_SERVICES + 50 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_mem_detailed_writeback); if(unlikely(!st_mem_detailed_pgfault)) { - st_mem_detailed_pgfault = rrdset_find_bytype("services", "mem_pgfault"); - if(likely(!st_mem_detailed_pgfault)) - st_mem_detailed_pgfault = rrdset_create("services", "mem_pgfault", NULL, "mem", "services.mem_pgfault", "Systemd Services Memory Minor Page Faults", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 60, update_every, RRDSET_TYPE_STACKED); + + st_mem_detailed_pgfault = rrdset_create_localhost( + "services" + , "mem_pgfault" + , NULL + , "mem" + , "services.mem_pgfault" + , "Systemd Services Memory Minor Page Faults" + , "MB/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 60 + , update_every + , RRDSET_TYPE_STACKED + ); } else rrdset_next(st_mem_detailed_pgfault); if(unlikely(!st_mem_detailed_pgmajfault)) { - st_mem_detailed_pgmajfault = rrdset_find_bytype("services", "mem_pgmajfault"); - if(likely(!st_mem_detailed_pgmajfault)) - st_mem_detailed_pgmajfault = rrdset_create("services", "mem_pgmajfault", NULL, "mem", "services.mem_pgmajfault", "Systemd Services Memory Major Page Faults", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 70, update_every, RRDSET_TYPE_STACKED); + + st_mem_detailed_pgmajfault = rrdset_create_localhost( + "services" + , "mem_pgmajfault" + , NULL + , "mem" + , "services.mem_pgmajfault" + , "Systemd Services Memory Major Page Faults" + , "MB/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 70 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_mem_detailed_pgmajfault); if(unlikely(!st_mem_detailed_pgpgin)) { - st_mem_detailed_pgpgin = rrdset_find_bytype("services", "mem_pgpgin"); - if(likely(!st_mem_detailed_pgpgin)) - st_mem_detailed_pgpgin = rrdset_create("services", "mem_pgpgin", NULL, "mem", "services.mem_pgpgin", "Systemd Services Memory Charging Activity", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 80, update_every, RRDSET_TYPE_STACKED); + + st_mem_detailed_pgpgin = rrdset_create_localhost( + "services" + , "mem_pgpgin" + , NULL + , "mem" + , "services.mem_pgpgin" + , "Systemd Services Memory Charging Activity" + , "MB/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 80 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_mem_detailed_pgpgin); if(unlikely(!st_mem_detailed_pgpgout)) { - st_mem_detailed_pgpgout = rrdset_find_bytype("services", "mem_pgpgout"); - if(likely(!st_mem_detailed_pgpgout)) - st_mem_detailed_pgpgout = rrdset_create("services", "mem_pgpgout", NULL, "mem", "services.mem_pgpgout", "Systemd Services Memory Uncharging Activity", "MB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 90, update_every, RRDSET_TYPE_STACKED); + + st_mem_detailed_pgpgout = rrdset_create_localhost( + "services" + , "mem_pgpgout" + , NULL + , "mem" + , "services.mem_pgpgout" + , "Systemd Services Memory Uncharging Activity" + , "MB/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 90 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_mem_detailed_pgpgout); @@ -1398,9 +1538,20 @@ void update_services_charts(int update_every, if(likely(do_mem_failcnt)) { if(unlikely(!st_mem_failcnt)) { - st_mem_failcnt = rrdset_find_bytype("services", "mem_failcnt"); - if(likely(!st_mem_failcnt)) - st_mem_failcnt = rrdset_create("services", "mem_failcnt", NULL, "mem", "services.mem_failcnt", "Systemd Services Memory Limit Failures", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 110, update_every, RRDSET_TYPE_STACKED); + + st_mem_failcnt = rrdset_create_localhost( + "services" + , "mem_failcnt" + , NULL + , "mem" + , "services.mem_failcnt" + , "Systemd Services Memory Limit Failures" + , "MB" + , CHART_PRIORITY_SYSTEMD_SERVICES + 110 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_mem_failcnt); @@ -1408,9 +1559,20 @@ void update_services_charts(int update_every, if(likely(do_swap_usage)) { if(unlikely(!st_swap_usage)) { - st_swap_usage = rrdset_find_bytype("services", "swap_usage"); - if(likely(!st_swap_usage)) - st_swap_usage = rrdset_create("services", "swap_usage", NULL, "swap", "services.swap_usage", "Systemd Services Swap Memory Used", "MB", CHART_PRIORITY_SYSTEMD_SERVICES + 100, update_every, RRDSET_TYPE_STACKED); + + st_swap_usage = rrdset_create_localhost( + "services" + , "swap_usage" + , NULL + , "swap" + , "services.swap_usage" + , "Systemd Services Swap Memory Used" + , "MB" + , CHART_PRIORITY_SYSTEMD_SERVICES + 100 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_swap_usage); @@ -1418,17 +1580,39 @@ void update_services_charts(int update_every, if(likely(do_io)) { if(unlikely(!st_io_read)) { - st_io_read = rrdset_find_bytype("services", "io_read"); - if(likely(!st_io_read)) - st_io_read = rrdset_create("services", "io_read", NULL, "disk", "services.io_read", "Systemd Services Disk Read Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 120, update_every, RRDSET_TYPE_STACKED); + + st_io_read = rrdset_create_localhost( + "services" + , "io_read" + , NULL + , "disk" + , "services.io_read" + , "Systemd Services Disk Read Bandwidth" + , "KB/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 120 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_io_read); if(unlikely(!st_io_write)) { - st_io_write = rrdset_find_bytype("services", "io_write"); - if(likely(!st_io_write)) - st_io_write = rrdset_create("services", "io_write", NULL, "disk", "services.io_write", "Systemd Services Disk Write Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 130, update_every, RRDSET_TYPE_STACKED); + + st_io_write = rrdset_create_localhost( + "services" + , "io_write" + , NULL + , "disk" + , "services.io_write" + , "Systemd Services Disk Write Bandwidth" + , "KB/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 130 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_io_write); @@ -1436,17 +1620,39 @@ void update_services_charts(int update_every, if(likely(do_io_ops)) { if(unlikely(!st_io_serviced_read)) { - st_io_serviced_read = rrdset_find_bytype("services", "io_ops_read"); - if(likely(!st_io_serviced_read)) - st_io_serviced_read = rrdset_create("services", "io_ops_read", NULL, "disk", "services.io_ops_read", "Systemd Services Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 140, update_every, RRDSET_TYPE_STACKED); + + st_io_serviced_read = rrdset_create_localhost( + "services" + , "io_ops_read" + , NULL + , "disk" + , "services.io_ops_read" + , "Systemd Services Disk Read Operations" + , "operations/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 140 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_io_serviced_read); if(unlikely(!st_io_serviced_write)) { - st_io_serviced_write = rrdset_find_bytype("services", "io_ops_write"); - if(likely(!st_io_serviced_write)) - st_io_serviced_write = rrdset_create("services", "io_ops_write", NULL, "disk", "services.io_ops_write", "Systemd Services Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 150, update_every, RRDSET_TYPE_STACKED); + + st_io_serviced_write = rrdset_create_localhost( + "services" + , "io_ops_write" + , NULL + , "disk" + , "services.io_ops_write" + , "Systemd Services Disk Write Operations" + , "operations/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 150 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_io_serviced_write); @@ -1454,17 +1660,39 @@ void update_services_charts(int update_every, if(likely(do_throttle_io)) { if(unlikely(!st_throttle_io_read)) { - st_throttle_io_read = rrdset_find_bytype("services", "throttle_io_read"); - if(likely(!st_throttle_io_read)) - st_throttle_io_read = rrdset_create("services", "throttle_io_read", NULL, "disk", "services.throttle_io_read", "Systemd Services Throttle Disk Read Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 160, update_every, RRDSET_TYPE_STACKED); + + st_throttle_io_read = rrdset_create_localhost( + "services" + , "throttle_io_read" + , NULL + , "disk" + , "services.throttle_io_read" + , "Systemd Services Throttle Disk Read Bandwidth" + , "KB/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 160 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_throttle_io_read); if(unlikely(!st_throttle_io_write)) { - st_throttle_io_write = rrdset_find_bytype("services", "throttle_io_write"); - if(likely(!st_throttle_io_write)) - st_throttle_io_write = rrdset_create("services", "throttle_io_write", NULL, "disk", "services.throttle_io_write", "Systemd Services Throttle Disk Write Bandwidth", "KB/s", CHART_PRIORITY_SYSTEMD_SERVICES + 170, update_every, RRDSET_TYPE_STACKED); + + st_throttle_io_write = rrdset_create_localhost( + "services" + , "throttle_io_write" + , NULL + , "disk" + , "services.throttle_io_write" + , "Systemd Services Throttle Disk Write Bandwidth" + , "KB/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 170 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_throttle_io_write); @@ -1472,17 +1700,39 @@ void update_services_charts(int update_every, if(likely(do_throttle_ops)) { if(unlikely(!st_throttle_ops_read)) { - st_throttle_ops_read = rrdset_find_bytype("services", "throttle_io_ops_read"); - if(likely(!st_throttle_ops_read)) - st_throttle_ops_read = rrdset_create("services", "throttle_io_ops_read", NULL, "disk", "services.throttle_io_ops_read", "Systemd Services Throttle Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 180, update_every, RRDSET_TYPE_STACKED); + + st_throttle_ops_read = rrdset_create_localhost( + "services" + , "throttle_io_ops_read" + , NULL + , "disk" + , "services.throttle_io_ops_read" + , "Systemd Services Throttle Disk Read Operations" + , "operations/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 180 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_throttle_ops_read); if(unlikely(!st_throttle_ops_write)) { - st_throttle_ops_write = rrdset_find_bytype("services", "throttle_io_ops_write"); - if(likely(!st_throttle_ops_write)) - st_throttle_ops_write = rrdset_create("services", "throttle_io_ops_write", NULL, "disk", "services.throttle_io_ops_write", "Systemd Services Throttle Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 190, update_every, RRDSET_TYPE_STACKED); + + st_throttle_ops_write = rrdset_create_localhost( + "services" + , "throttle_io_ops_write" + , NULL + , "disk" + , "services.throttle_io_ops_write" + , "Systemd Services Throttle Disk Write Operations" + , "operations/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 190 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_throttle_ops_write); @@ -1490,17 +1740,39 @@ void update_services_charts(int update_every, if(likely(do_queued_ops)) { if(unlikely(!st_queued_ops_read)) { - st_queued_ops_read = rrdset_find_bytype("services", "queued_io_ops_read"); - if(likely(!st_queued_ops_read)) - st_queued_ops_read = rrdset_create("services", "queued_io_ops_read", NULL, "disk", "services.queued_io_ops_read", "Systemd Services Queued Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 200, update_every, RRDSET_TYPE_STACKED); + + st_queued_ops_read = rrdset_create_localhost( + "services" + , "queued_io_ops_read" + , NULL + , "disk" + , "services.queued_io_ops_read" + , "Systemd Services Queued Disk Read Operations" + , "operations/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 200 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_queued_ops_read); if(unlikely(!st_queued_ops_write)) { - st_queued_ops_write = rrdset_find_bytype("services", "queued_io_ops_write"); - if(likely(!st_queued_ops_write)) - st_queued_ops_write = rrdset_create("services", "queued_io_ops_write", NULL, "disk", "services.queued_io_ops_write", "Systemd Services Queued Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 210, update_every, RRDSET_TYPE_STACKED); + + st_queued_ops_write = rrdset_create_localhost( + "services" + , "queued_io_ops_write" + , NULL + , "disk" + , "services.queued_io_ops_write" + , "Systemd Services Queued Disk Write Operations" + , "operations/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 210 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_queued_ops_write); @@ -1508,17 +1780,39 @@ void update_services_charts(int update_every, if(likely(do_merged_ops)) { if(unlikely(!st_merged_ops_read)) { - st_merged_ops_read = rrdset_find_bytype("services", "merged_io_ops_read"); - if(likely(!st_merged_ops_read)) - st_merged_ops_read = rrdset_create("services", "merged_io_ops_read", NULL, "disk", "services.merged_io_ops_read", "Systemd Services Merged Disk Read Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 220, update_every, RRDSET_TYPE_STACKED); + + st_merged_ops_read = rrdset_create_localhost( + "services" + , "merged_io_ops_read" + , NULL + , "disk" + , "services.merged_io_ops_read" + , "Systemd Services Merged Disk Read Operations" + , "operations/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 220 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_merged_ops_read); if(unlikely(!st_merged_ops_write)) { - st_merged_ops_write = rrdset_find_bytype("services", "merged_io_ops_write"); - if(likely(!st_merged_ops_write)) - st_merged_ops_write = rrdset_create("services", "merged_io_ops_write", NULL, "disk", "services.merged_io_ops_write", "Systemd Services Merged Disk Write Operations", "operations/s", CHART_PRIORITY_SYSTEMD_SERVICES + 230, update_every, RRDSET_TYPE_STACKED); + + st_merged_ops_write = rrdset_create_localhost( + "services" + , "merged_io_ops_write" + , NULL + , "disk" + , "services.merged_io_ops_write" + , "Systemd Services Merged Disk Write Operations" + , "operations/s" + , CHART_PRIORITY_SYSTEMD_SERVICES + 230 + , update_every + , RRDSET_TYPE_STACKED + ); + } else rrdset_next(st_merged_ops_write); @@ -1532,134 +1826,142 @@ void update_services_charts(int update_every, if(likely(do_cpu && cg->cpuacct_stat.updated)) { if(unlikely(!cg->rd_cpu)) - cg->rd_cpu = rrddim_add(st_cpu, cg->chart_id, cg->chart_title, 100, hz, RRDDIM_INCREMENTAL); + cg->rd_cpu = rrddim_add(st_cpu, cg->chart_id, cg->chart_title, 100, hz, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_cpu, cg->rd_cpu, cg->cpuacct_stat.user + cg->cpuacct_stat.system); } if(likely(do_mem_usage && cg->memory.updated_usage_in_bytes)) { if(unlikely(!cg->rd_mem_usage)) - cg->rd_mem_usage = rrddim_add(st_mem_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + cg->rd_mem_usage = rrddim_add(st_mem_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); rrddim_set_by_pointer(st_mem_usage, cg->rd_mem_usage, cg->memory.usage_in_bytes - ((cgroup_used_memory_without_cache)?cg->memory.cache:0)); } if(likely(do_mem_detailed && cg->memory.updated_detailed)) { if(unlikely(!cg->rd_mem_detailed_rss)) - cg->rd_mem_detailed_rss = rrddim_add(st_mem_detailed_rss, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + cg->rd_mem_detailed_rss = rrddim_add(st_mem_detailed_rss, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_set_by_pointer(st_mem_detailed_rss, cg->rd_mem_detailed_rss, cg->memory.rss + cg->memory.rss_huge); if(unlikely(!cg->rd_mem_detailed_mapped)) - cg->rd_mem_detailed_mapped = rrddim_add(st_mem_detailed_mapped, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + cg->rd_mem_detailed_mapped = rrddim_add(st_mem_detailed_mapped, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_set_by_pointer(st_mem_detailed_mapped, cg->rd_mem_detailed_mapped, cg->memory.mapped_file); if(unlikely(!cg->rd_mem_detailed_cache)) - cg->rd_mem_detailed_cache = rrddim_add(st_mem_detailed_cache, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + cg->rd_mem_detailed_cache = rrddim_add(st_mem_detailed_cache, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_set_by_pointer(st_mem_detailed_cache, cg->rd_mem_detailed_cache, cg->memory.cache); if(unlikely(!cg->rd_mem_detailed_writeback)) - cg->rd_mem_detailed_writeback = rrddim_add(st_mem_detailed_writeback, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + cg->rd_mem_detailed_writeback = rrddim_add(st_mem_detailed_writeback, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_set_by_pointer(st_mem_detailed_writeback, cg->rd_mem_detailed_writeback, cg->memory.writeback); if(unlikely(!cg->rd_mem_detailed_pgfault)) - cg->rd_mem_detailed_pgfault = rrddim_add(st_mem_detailed_pgfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + cg->rd_mem_detailed_pgfault = rrddim_add(st_mem_detailed_pgfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_set_by_pointer(st_mem_detailed_pgfault, cg->rd_mem_detailed_pgfault, cg->memory.pgfault); if(unlikely(!cg->rd_mem_detailed_pgmajfault)) - cg->rd_mem_detailed_pgmajfault = rrddim_add(st_mem_detailed_pgmajfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + cg->rd_mem_detailed_pgmajfault = rrddim_add(st_mem_detailed_pgmajfault, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_set_by_pointer(st_mem_detailed_pgmajfault, cg->rd_mem_detailed_pgmajfault, cg->memory.pgmajfault); if(unlikely(!cg->rd_mem_detailed_pgpgin)) - cg->rd_mem_detailed_pgpgin = rrddim_add(st_mem_detailed_pgpgin, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + cg->rd_mem_detailed_pgpgin = rrddim_add(st_mem_detailed_pgpgin, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_set_by_pointer(st_mem_detailed_pgpgin, cg->rd_mem_detailed_pgpgin, cg->memory.pgpgin); if(unlikely(!cg->rd_mem_detailed_pgpgout)) - cg->rd_mem_detailed_pgpgout = rrddim_add(st_mem_detailed_pgpgout, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + cg->rd_mem_detailed_pgpgout = rrddim_add(st_mem_detailed_pgpgout, cg->chart_id, cg->chart_title, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_set_by_pointer(st_mem_detailed_pgpgout, cg->rd_mem_detailed_pgpgout, cg->memory.pgpgout); } if(likely(do_mem_failcnt && cg->memory.updated_failcnt)) { if(unlikely(!cg->rd_mem_failcnt)) - cg->rd_mem_failcnt = rrddim_add(st_mem_failcnt, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + cg->rd_mem_failcnt = rrddim_add(st_mem_failcnt, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_mem_failcnt, cg->rd_mem_failcnt, cg->memory.failcnt); } if(likely(do_swap_usage && cg->memory.updated_msw_usage_in_bytes)) { if(unlikely(!cg->rd_swap_usage)) - cg->rd_swap_usage = rrddim_add(st_swap_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + cg->rd_swap_usage = rrddim_add(st_swap_usage, cg->chart_id, cg->chart_title, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); rrddim_set_by_pointer(st_swap_usage, cg->rd_swap_usage, cg->memory.msw_usage_in_bytes); } if(likely(do_io && cg->io_service_bytes.updated)) { if(unlikely(!cg->rd_io_service_bytes_read)) - cg->rd_io_service_bytes_read = rrddim_add(st_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL); + cg->rd_io_service_bytes_read = rrddim_add(st_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_io_read, cg->rd_io_service_bytes_read, cg->io_service_bytes.Read); if(unlikely(!cg->rd_io_service_bytes_write)) - cg->rd_io_service_bytes_write = rrddim_add(st_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL); + cg->rd_io_service_bytes_write = rrddim_add(st_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_io_write, cg->rd_io_service_bytes_write, cg->io_service_bytes.Write); } if(likely(do_io_ops && cg->io_serviced.updated)) { if(unlikely(!cg->rd_io_serviced_read)) - cg->rd_io_serviced_read = rrddim_add(st_io_serviced_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + cg->rd_io_serviced_read = rrddim_add(st_io_serviced_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_io_serviced_read, cg->rd_io_serviced_read, cg->io_serviced.Read); if(unlikely(!cg->rd_io_serviced_write)) - cg->rd_io_serviced_write = rrddim_add(st_io_serviced_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + cg->rd_io_serviced_write = rrddim_add(st_io_serviced_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_io_serviced_write, cg->rd_io_serviced_write, cg->io_serviced.Write); } if(likely(do_throttle_io && cg->throttle_io_service_bytes.updated)) { if(unlikely(!cg->rd_throttle_io_read)) - cg->rd_throttle_io_read = rrddim_add(st_throttle_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL); + cg->rd_throttle_io_read = rrddim_add(st_throttle_io_read, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_throttle_io_read, cg->rd_throttle_io_read, cg->throttle_io_service_bytes.Read); if(unlikely(!cg->rd_throttle_io_write)) - cg->rd_throttle_io_write = rrddim_add(st_throttle_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRDDIM_INCREMENTAL); + cg->rd_throttle_io_write = rrddim_add(st_throttle_io_write, cg->chart_id, cg->chart_title, 1, 1024, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_throttle_io_write, cg->rd_throttle_io_write, cg->throttle_io_service_bytes.Write); } if(likely(do_throttle_ops && cg->throttle_io_serviced.updated)) { if(unlikely(!cg->rd_throttle_io_serviced_read)) - cg->rd_throttle_io_serviced_read = rrddim_add(st_throttle_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + cg->rd_throttle_io_serviced_read = rrddim_add(st_throttle_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_throttle_ops_read, cg->rd_throttle_io_serviced_read, cg->throttle_io_serviced.Read); if(unlikely(!cg->rd_throttle_io_serviced_write)) - cg->rd_throttle_io_serviced_write = rrddim_add(st_throttle_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + cg->rd_throttle_io_serviced_write = rrddim_add(st_throttle_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_throttle_ops_write, cg->rd_throttle_io_serviced_write, cg->throttle_io_serviced.Write); } if(likely(do_queued_ops && cg->io_queued.updated)) { if(unlikely(!cg->rd_io_queued_read)) - cg->rd_io_queued_read = rrddim_add(st_queued_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + cg->rd_io_queued_read = rrddim_add(st_queued_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_queued_ops_read, cg->rd_io_queued_read, cg->io_queued.Read); if(unlikely(!cg->rd_io_queued_write)) - cg->rd_io_queued_write = rrddim_add(st_queued_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + cg->rd_io_queued_write = rrddim_add(st_queued_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_queued_ops_write, cg->rd_io_queued_write, cg->io_queued.Write); } if(likely(do_merged_ops && cg->io_merged.updated)) { if(unlikely(!cg->rd_io_merged_read)) - cg->rd_io_merged_read = rrddim_add(st_merged_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + cg->rd_io_merged_read = rrddim_add(st_merged_ops_read, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_merged_ops_read, cg->rd_io_merged_read, cg->io_merged.Read); if(unlikely(!cg->rd_io_merged_write)) - cg->rd_io_merged_write = rrddim_add(st_merged_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRDDIM_INCREMENTAL); + cg->rd_io_merged_write = rrddim_add(st_merged_ops_write, cg->chart_id, cg->chart_title, 1, 1, RRD_ALGORITHM_INCREMENTAL); rrddim_set_by_pointer(st_merged_ops_write, cg->rd_io_merged_write, cg->io_merged.Write); } @@ -1756,33 +2058,43 @@ void update_cgroup_charts(int update_every) { continue; if(likely(cgroup_enable_systemd_services && cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)) { - if(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_ONDEMAND_YES) services_do_cpu++; + if(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_BOOLEAN_YES) services_do_cpu++; if(cgroup_enable_systemd_services_detailed_memory && cg->memory.updated_detailed && cg->memory.enabled_detailed) services_do_mem_detailed++; - if(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_ONDEMAND_YES) services_do_mem_usage++; - if(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_ONDEMAND_YES) services_do_mem_failcnt++; - if(cg->memory.updated_msw_usage_in_bytes && cg->memory.enabled_msw_usage_in_bytes == CONFIG_ONDEMAND_YES) services_do_swap_usage++; - - if(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_ONDEMAND_YES) services_do_io++; - if(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_ONDEMAND_YES) services_do_io_ops++; - if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_ONDEMAND_YES) services_do_throttle_io++; - if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_ONDEMAND_YES) services_do_throttle_ops++; - if(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_ONDEMAND_YES) services_do_queued_ops++; - if(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_ONDEMAND_YES) services_do_merged_ops++; + if(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_BOOLEAN_YES) services_do_mem_usage++; + if(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_BOOLEAN_YES) services_do_mem_failcnt++; + if(cg->memory.updated_msw_usage_in_bytes && cg->memory.enabled_msw_usage_in_bytes == CONFIG_BOOLEAN_YES) services_do_swap_usage++; + + if(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_BOOLEAN_YES) services_do_io++; + if(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_BOOLEAN_YES) services_do_io_ops++; + if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_BOOLEAN_YES) services_do_throttle_io++; + if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_BOOLEAN_YES) services_do_throttle_ops++; + if(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_BOOLEAN_YES) services_do_queued_ops++; + if(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_BOOLEAN_YES) services_do_merged_ops++; continue; } type[0] = '\0'; - if(likely(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_ONDEMAND_YES)) { + if(likely(cg->cpuacct_stat.updated && cg->cpuacct_stat.enabled == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_cpu)) { - cg->st_cpu = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu"); - if(likely(!cg->st_cpu)) { - snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title); - cg->st_cpu = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", CHART_PRIORITY_CONTAINERS, update_every, RRDSET_TYPE_STACKED); - } - rrddim_add(cg->st_cpu, "user", NULL, 100, hz, RRDDIM_INCREMENTAL); - rrddim_add(cg->st_cpu, "system", NULL, 100, hz, RRDDIM_INCREMENTAL); + snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title); + + cg->st_cpu = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "cpu" + , NULL + , "cpu" + , "cgroup.cpu" + , title + , "%" + , CHART_PRIORITY_CONTAINERS + , update_every + , RRDSET_TYPE_STACKED + ); + + rrddim_add(cg->st_cpu, "user", NULL, 100, hz, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(cg->st_cpu, "system", NULL, 100, hz, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(cg->st_cpu); @@ -1792,84 +2104,128 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_cpu); } - if(likely(cg->cpuacct_usage.updated && cg->cpuacct_usage.enabled == CONFIG_ONDEMAND_YES)) { + if(likely(cg->cpuacct_usage.updated && cg->cpuacct_usage.enabled == CONFIG_BOOLEAN_YES)) { char id[RRD_ID_LENGTH_MAX + 1]; unsigned int i; if(unlikely(!cg->st_cpu_per_core)) { - cg->st_cpu_per_core = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu_per_core"); - if(likely(!cg->st_cpu_per_core)) { - snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) Per Core for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title); - cg->st_cpu_per_core = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", CHART_PRIORITY_CONTAINERS + 100, update_every, RRDSET_TYPE_STACKED); - } + snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) Per Core for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title); + + cg->st_cpu_per_core = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "cpu_per_core" + , NULL + , "cpu" + , "cgroup.cpu_per_core" + , title + , "%" + , CHART_PRIORITY_CONTAINERS + 100 + , update_every + , RRDSET_TYPE_STACKED + ); + for(i = 0; i < cg->cpuacct_usage.cpus; i++) { - snprintfz(id, CHART_TITLE_MAX, "cpu%u", i); - rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL); + snprintfz(id, RRD_ID_LENGTH_MAX, "cpu%u", i); + rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRD_ALGORITHM_INCREMENTAL); } } else rrdset_next(cg->st_cpu_per_core); for(i = 0; i < cg->cpuacct_usage.cpus ;i++) { - snprintfz(id, CHART_TITLE_MAX, "cpu%u", i); + snprintfz(id, RRD_ID_LENGTH_MAX, "cpu%u", i); rrddim_set(cg->st_cpu_per_core, id, cg->cpuacct_usage.cpu_percpu[i]); } rrdset_done(cg->st_cpu_per_core); } - if(likely(cg->memory.updated_detailed && cg->memory.enabled_detailed == CONFIG_ONDEMAND_YES)) { + if(likely(cg->memory.updated_detailed && cg->memory.enabled_detailed == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_mem)) { - cg->st_mem = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem"); - if(likely(!cg->st_mem)) { - snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title); - cg->st_mem = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", CHART_PRIORITY_CONTAINERS + 210, update_every, RRDSET_TYPE_STACKED); - } + snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title); + + cg->st_mem = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "mem" + , NULL + , "mem" + , "cgroup.mem" + , title + , "MB" + , CHART_PRIORITY_CONTAINERS + 210 + , update_every + , RRDSET_TYPE_STACKED + ); + + rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); - rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); if(cg->memory.detailed_has_swap) - rrddim_add(cg->st_mem, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(cg->st_mem, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(cg->st_mem, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_add(cg->st_mem, "swap", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + + rrddim_add(cg->st_mem, "rss_huge", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(cg->st_mem, "mapped_file", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(cg->st_mem); rrddim_set(cg->st_mem, "cache", cg->memory.cache); rrddim_set(cg->st_mem, "rss", cg->memory.rss); + if(cg->memory.detailed_has_swap) rrddim_set(cg->st_mem, "swap", cg->memory.swap); + rrddim_set(cg->st_mem, "rss_huge", cg->memory.rss_huge); rrddim_set(cg->st_mem, "mapped_file", cg->memory.mapped_file); rrdset_done(cg->st_mem); if(unlikely(!cg->st_writeback)) { - cg->st_writeback = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "writeback"); - if(likely(!cg->st_writeback)) { - snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title); - cg->st_writeback = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", CHART_PRIORITY_CONTAINERS + 300, update_every, RRDSET_TYPE_AREA); - } + snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title); + + cg->st_writeback = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "writeback" + , NULL + , "mem" + , "cgroup.writeback" + , title + , "MB" + , CHART_PRIORITY_CONTAINERS + 300 + , update_every + , RRDSET_TYPE_AREA + ); if(cg->memory.detailed_has_dirty) - rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + + rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(cg->st_writeback); if(cg->memory.detailed_has_dirty) rrddim_set(cg->st_writeback, "dirty", cg->memory.dirty); + rrddim_set(cg->st_writeback, "writeback", cg->memory.writeback); rrdset_done(cg->st_writeback); if(unlikely(!cg->st_mem_activity)) { - cg->st_mem_activity = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_activity"); - if(likely(!cg->st_mem_activity)) { - snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title); - cg->st_mem_activity = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s", CHART_PRIORITY_CONTAINERS + 400, update_every, RRDSET_TYPE_LINE); - } - rrddim_add(cg->st_mem_activity, "pgpgin", "in", system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); - rrddim_add(cg->st_mem_activity, "pgpgout", "out", -system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title); + + cg->st_mem_activity = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "mem_activity" + , NULL + , "mem" + , "cgroup.mem_activity" + , title + , "MB/s" + , CHART_PRIORITY_CONTAINERS + 400 + , update_every + , RRDSET_TYPE_LINE + ); + + rrddim_add(cg->st_mem_activity, "pgpgin", "in", system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(cg->st_mem_activity, "pgpgout", "out", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(cg->st_mem_activity); @@ -1879,13 +2235,23 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_mem_activity); if(unlikely(!cg->st_pgfaults)) { - cg->st_pgfaults = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "pgfaults"); - if(likely(!cg->st_pgfaults)) { - snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title); - cg->st_pgfaults = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", CHART_PRIORITY_CONTAINERS + 500, update_every, RRDSET_TYPE_LINE); - } - rrddim_add(cg->st_pgfaults, "pgfault", NULL, system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); - rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -system_page_size, 1024 * 1024, RRDDIM_INCREMENTAL); + snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title); + + cg->st_pgfaults = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "pgfaults" + , NULL + , "mem" + , "cgroup.pgfaults" + , title + , "MB/s" + , CHART_PRIORITY_CONTAINERS + 500 + , update_every + , RRDSET_TYPE_LINE + ); + + rrddim_add(cg->st_pgfaults, "pgfault", NULL, system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -system_page_size, 1024 * 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(cg->st_pgfaults); @@ -1895,15 +2261,25 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_pgfaults); } - if(likely(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_ONDEMAND_YES)) { + if(likely(cg->memory.updated_usage_in_bytes && cg->memory.enabled_usage_in_bytes == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_mem_usage)) { - cg->st_mem_usage = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_usage"); - if(likely(!cg->st_mem_usage)) { - snprintfz(title, CHART_TITLE_MAX, "Used Memory %sfor cgroup %s", (cgroup_used_memory_without_cache && cg->memory.updated_detailed)?"without Cache ":"", cg->chart_title); - cg->st_mem_usage = rrdset_create(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title, "MB", CHART_PRIORITY_CONTAINERS + 200, update_every, RRDSET_TYPE_STACKED); - } - rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); - rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE); + snprintfz(title, CHART_TITLE_MAX, "Used Memory %sfor cgroup %s", (cgroup_used_memory_without_cache && cg->memory.updated_detailed)?"without Cache ":"", cg->chart_title); + + cg->st_mem_usage = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "mem_usage" + , NULL + , "mem" + , "cgroup.mem_usage" + , title + , "MB" + , CHART_PRIORITY_CONTAINERS + 200 + , update_every + , RRDSET_TYPE_STACKED + ); + + rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(cg->st_mem_usage); @@ -1913,14 +2289,24 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_mem_usage); } - if(likely(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_ONDEMAND_YES)) { + if(likely(cg->memory.updated_failcnt && cg->memory.enabled_failcnt == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_mem_failcnt)) { - cg->st_mem_failcnt = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_failcnt"); - if(likely(!cg->st_mem_failcnt)) { - snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title); - cg->st_mem_failcnt = rrdset_create(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt", title, "count", CHART_PRIORITY_CONTAINERS + 250, update_every, RRDSET_TYPE_LINE); - } - rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRDDIM_INCREMENTAL); + snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title); + + cg->st_mem_failcnt = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "mem_failcnt" + , NULL + , "mem" + , "cgroup.mem_failcnt" + , title + , "count" + , CHART_PRIORITY_CONTAINERS + 250 + , update_every + , RRDSET_TYPE_LINE + ); + + rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(cg->st_mem_failcnt); @@ -1929,15 +2315,25 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_mem_failcnt); } - if(likely(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_ONDEMAND_YES)) { + if(likely(cg->io_service_bytes.updated && cg->io_service_bytes.enabled == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_io)) { - cg->st_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "io"); - if(likely(!cg->st_io)) { - snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title); - cg->st_io = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_AREA); - } - rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL); - rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL); + snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title); + + cg->st_io = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "io" + , NULL + , "disk" + , "cgroup.io" + , title + , "KB/s" + , CHART_PRIORITY_CONTAINERS + 1200 + , update_every + , RRDSET_TYPE_AREA + ); + + rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(cg->st_io); @@ -1947,15 +2343,25 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_io); } - if(likely(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_ONDEMAND_YES)) { + if(likely(cg->io_serviced.updated && cg->io_serviced.enabled == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_serviced_ops)) { - cg->st_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "serviced_ops"); - if(likely(!cg->st_serviced_ops)) { - snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title); - cg->st_serviced_ops = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_LINE); - } - rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL); + snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title); + + cg->st_serviced_ops = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "serviced_ops" + , NULL + , "disk" + , "cgroup.serviced_ops" + , title + , "operations/s" + , CHART_PRIORITY_CONTAINERS + 1200 + , update_every + , RRDSET_TYPE_LINE + ); + + rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(cg->st_serviced_ops); @@ -1965,15 +2371,25 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_serviced_ops); } - if(likely(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_ONDEMAND_YES)) { + if(likely(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.enabled == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_throttle_io)) { - cg->st_throttle_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_io"); - if(likely(!cg->st_throttle_io)) { - snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title); - cg->st_throttle_io = rrdset_create(type, "throttle_io", NULL, "disk", "cgroup.throttle_io", title, "KB/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_AREA); - } - rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL); - rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL); + snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title); + + cg->st_throttle_io = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "throttle_io" + , NULL + , "disk" + , "cgroup.throttle_io" + , title + , "KB/s" + , CHART_PRIORITY_CONTAINERS + 1200 + , update_every + , RRDSET_TYPE_AREA + ); + + rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(cg->st_throttle_io); @@ -1983,15 +2399,25 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_throttle_io); } - if(likely(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_ONDEMAND_YES)) { + if(likely(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.enabled == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_throttle_serviced_ops)) { - cg->st_throttle_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_serviced_ops"); - if(likely(!cg->st_throttle_serviced_ops)) { - snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title); - cg->st_throttle_serviced_ops = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", CHART_PRIORITY_CONTAINERS + 1200, update_every, RRDSET_TYPE_LINE); - } - rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL); + snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title); + + cg->st_throttle_serviced_ops = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "throttle_serviced_ops" + , NULL + , "disk" + , "cgroup.throttle_serviced_ops" + , title + , "operations/s" + , CHART_PRIORITY_CONTAINERS + 1200 + , update_every + , RRDSET_TYPE_LINE + ); + + rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(cg->st_throttle_serviced_ops); @@ -2001,15 +2427,25 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_throttle_serviced_ops); } - if(likely(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_ONDEMAND_YES)) { + if(likely(cg->io_queued.updated && cg->io_queued.enabled == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_queued_ops)) { - cg->st_queued_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "queued_ops"); - if(likely(!cg->st_queued_ops)) { - snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title); - cg->st_queued_ops = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", CHART_PRIORITY_CONTAINERS + 2000, update_every, RRDSET_TYPE_LINE); - } - rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRDDIM_ABSOLUTE); - rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRDDIM_ABSOLUTE); + snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title); + + cg->st_queued_ops = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "queued_ops" + , NULL + , "disk" + , "cgroup.queued_ops" + , title + , "operations" + , CHART_PRIORITY_CONTAINERS + 2000 + , update_every + , RRDSET_TYPE_LINE + ); + + rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(cg->st_queued_ops); @@ -2019,15 +2455,25 @@ void update_cgroup_charts(int update_every) { rrdset_done(cg->st_queued_ops); } - if(likely(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_ONDEMAND_YES)) { + if(likely(cg->io_merged.updated && cg->io_merged.enabled == CONFIG_BOOLEAN_YES)) { if(unlikely(!cg->st_merged_ops)) { - cg->st_merged_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "merged_ops"); - if(likely(!cg->st_merged_ops)) { - snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title); - cg->st_merged_ops = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", CHART_PRIORITY_CONTAINERS + 2100, update_every, RRDSET_TYPE_LINE); - } - rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL); - rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL); + snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title); + + cg->st_merged_ops = rrdset_create_localhost( + cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX) + , "merged_ops" + , NULL + , "disk" + , "cgroup.merged_ops" + , title + , "operations/s" + , CHART_PRIORITY_CONTAINERS + 2100 + , update_every + , RRDSET_TYPE_LINE + ); + + rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(cg->st_merged_ops); @@ -2039,18 +2485,10 @@ void update_cgroup_charts(int update_every) { } if(likely(cgroup_enable_systemd_services)) - update_services_charts(update_every, - services_do_cpu, - services_do_mem_usage, - services_do_mem_detailed, - services_do_mem_failcnt, - services_do_swap_usage, - services_do_io, - services_do_io_ops, - services_do_throttle_io, - services_do_throttle_ops, - services_do_queued_ops, - services_do_merged_ops + update_systemd_services_charts(update_every, services_do_cpu, services_do_mem_usage, services_do_mem_detailed + , services_do_mem_failcnt, services_do_swap_usage, services_do_io + , services_do_io_ops, services_do_throttle_io, services_do_throttle_ops + , services_do_queued_ops, services_do_merged_ops ); debug(D_CGROUP, "done updating cgroups charts"); @@ -2079,24 +2517,21 @@ void *cgroups_main(void *ptr) { RRDSET *stcpu_thread = NULL; + heartbeat_t hb; + heartbeat_init(&hb); usec_t step = cgroup_update_every * USEC_PER_SEC; - usec_t find_every = cgroup_check_for_new_every * USEC_PER_SEC, find_next = 0; + usec_t find_every = cgroup_check_for_new_every * USEC_PER_SEC, find_dt = 0; for(;;) { - usec_t now = now_monotonic_usec(); - usec_t next = now - (now % step) + step; - - while(now < next) { - sleep_usec(next - now); - now = now_monotonic_usec(); - } - + usec_t hb_dt = heartbeat_next(&hb, step); if(unlikely(netdata_exit)) break; // BEGIN -- the job to be done - if(unlikely(now >= find_next)) { + find_dt += hb_dt; + if(unlikely(find_dt >= find_every || cgroups_check)) { find_all_cgroups(); - find_next = now + find_every; + find_dt = 0; + cgroups_check = 0; } read_all_cgroups(cgroup_root); @@ -2110,12 +2545,22 @@ void *cgroups_main(void *ptr) { getrusage(RUSAGE_THREAD, &thread); if(unlikely(!stcpu_thread)) { - stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu"); - if(unlikely(!stcpu_thread)) - stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, cgroup_update_every, RRDSET_TYPE_STACKED); - rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); - rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); + stcpu_thread = rrdset_create_localhost( + "netdata" + , "plugin_cgroups_cpu" + , NULL + , "cgroups" + , NULL + , "NetData CGroups Plugin CPU usage" + , "milliseconds/s" + , 132000 + , cgroup_update_every + , RRDSET_TYPE_STACKED + ); + + rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(stcpu_thread); diff --git a/src/sys_kernel_mm_ksm.c b/src/sys_kernel_mm_ksm.c index 83da74429..76d808538 100644 --- a/src/sys_kernel_mm_ksm.c +++ b/src/sys_kernel_mm_ksm.c @@ -20,40 +20,39 @@ KSM_NAME_VALUE values[] = { }; int do_sys_kernel_mm_ksm(int update_every, usec_t dt) { + (void)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; - 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%s", netdata_configured_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%s", netdata_configured_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%s", netdata_configured_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%s", netdata_configured_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%s", netdata_configured_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); } @@ -91,15 +90,16 @@ int do_sys_kernel_mm_ksm(int update_every, usec_t dt) { // -------------------------------------------------------------------- - st = rrdset_find("mem.ksm"); + st = rrdset_find_localhost("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); + st = rrdset_create_localhost("mem", "ksm", NULL, "ksm", NULL, "Kernel Same Page Merging", "MB", 5000 + , update_every, RRDSET_TYPE_AREA); + + rrddim_add(st, "shared", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "unshared", NULL, -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "sharing", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "volatile", NULL, -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "to_scan", "to scan", -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -110,12 +110,13 @@ int do_sys_kernel_mm_ksm(int update_every, usec_t dt) { rrddim_set(st, "to_scan", pages_to_scan * page_size); rrdset_done(st); - st = rrdset_find("mem.ksm_savings"); + st = rrdset_find_localhost("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_create_localhost("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); + rrddim_add(st, "savings", NULL, -1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rrddim_add(st, "offered", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); @@ -123,11 +124,12 @@ int do_sys_kernel_mm_ksm(int update_every, usec_t dt) { rrddim_set(st, "offered", offered * page_size); rrdset_done(st); - st = rrdset_find("mem.ksm_ratios"); + st = rrdset_find_localhost("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); + st = rrdset_create_localhost("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); + rrddim_add(st, "savings", NULL, 1, 10000, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(st); diff --git a/src/unit_test.c b/src/unit_test.c index 4e2f10c0a..0866d215c 100644 --- a/src/unit_test.c +++ b/src/unit_test.c @@ -282,7 +282,7 @@ struct test test1 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_ABSOLUTE, // algorithm + RRD_ALGORITHM_ABSOLUTE, // algorithm 10, // feed entries 9, // result entries test1_feed, // feed @@ -318,7 +318,7 @@ struct test test2 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_ABSOLUTE, // algorithm + RRD_ALGORITHM_ABSOLUTE, // algorithm 10, // feed entries 9, // result entries test2_feed, // feed @@ -353,7 +353,7 @@ struct test test3 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 10, // feed entries 9, // result entries test3_feed, // feed @@ -388,7 +388,7 @@ struct test test4 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 10, // feed entries 9, // result entries test4_feed, // feed @@ -423,7 +423,7 @@ struct test test5 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 10, // feed entries 9, // result entries test5_feed, // feed @@ -464,7 +464,7 @@ struct test test6 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 16, // feed entries 4, // result entries test6_feed, // feed @@ -499,7 +499,7 @@ struct test test7 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 10, // feed entries 18, // result entries test7_feed, // feed @@ -530,7 +530,7 @@ struct test test8 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_ABSOLUTE, // algorithm + RRD_ALGORITHM_ABSOLUTE, // algorithm 6, // feed entries 10, // result entries test8_feed, // feed @@ -571,7 +571,7 @@ struct test test9 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_ABSOLUTE, // algorithm + RRD_ALGORITHM_ABSOLUTE, // algorithm 16, // feed entries 4, // result entries test9_feed, // feed @@ -606,7 +606,7 @@ struct test test10 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 10, // feed entries 7, // result entries test10_feed, // feed @@ -649,7 +649,7 @@ struct test test11 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_PCENT_OVER_DIFF_TOTAL, // algorithm + RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL, // algorithm 10, // feed entries 9, // result entries test11_feed, // feed @@ -692,7 +692,7 @@ struct test test12 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_PCENT_OVER_DIFF_TOTAL, // algorithm + RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL, // algorithm 10, // feed entries 9, // result entries test12_feed, // feed @@ -727,7 +727,7 @@ struct test test13 = { 1, // update_every 1, // multiplier 1, // divisor - RRDDIM_PCENT_OVER_DIFF_TOTAL, // algorithm + RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL, // algorithm 10, // feed entries 7, // result entries test13_feed, // feed @@ -762,7 +762,7 @@ struct test test14 = { 30, // update_every 8, // multiplier 1000000000, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 10, // feed entries 8, // result entries test14_feed, // feed @@ -794,7 +794,7 @@ struct test test14b = { 30, // update_every 1, // multiplier 1, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 10, // feed entries 8, // result entries test14b_feed, // feed @@ -826,7 +826,7 @@ struct test test14c = { 30, // update_every 1, // multiplier 1, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 10, // feed entries 9, // result entries test14c_feed, // feed @@ -869,7 +869,7 @@ struct test test15 = { 1, // update_every 8, // multiplier 1024, // divisor - RRDDIM_INCREMENTAL, // algorithm + RRD_ALGORITHM_INCREMENTAL, // algorithm 10, // feed entries 9, // result entries test15_feed, // feed @@ -884,21 +884,22 @@ 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; + default_rrd_memory_mode = RRD_MEMORY_MODE_RAM; + default_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, test->update_every, RRDSET_TYPE_LINE); + RRDSET *st = rrdset_create_localhost("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1 + , test->update_every, 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; + rrdset_flag_set(st, RRDSET_FLAG_DEBUG); // feed it with the test data time_t time_now = 0, time_start = now_realtime_sec(); @@ -977,15 +978,16 @@ int run_test(struct test *test) static int test_variable_renames(void) { fprintf(stderr, "Creating chart\n"); - RRDSET *st = rrdset_create("chart", "ID", NULL, "family", "context", "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE); + RRDSET *st = rrdset_create_localhost("chart", "ID", NULL, "family", "context", "Unit Testing", "a value", 1, 1 + , RRDSET_TYPE_LINE); fprintf(stderr, "Created chart with id '%s', name '%s'\n", st->id, st->name); fprintf(stderr, "Creating dimension DIM1\n"); - RRDDIM *rd1 = rrddim_add(st, "DIM1", NULL, 1, 1, RRDDIM_INCREMENTAL); + RRDDIM *rd1 = rrddim_add(st, "DIM1", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd1->id, rd1->name); fprintf(stderr, "Creating dimension DIM2\n"); - RRDDIM *rd2 = rrddim_add(st, "DIM2", NULL, 1, 1, RRDDIM_INCREMENTAL); + RRDDIM *rd2 = rrddim_add(st, "DIM2", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd2->id, rd2->name); fprintf(stderr, "Renaming chart to CHARTNAME1\n"); @@ -1089,26 +1091,27 @@ int unit_test(long delay, long shift) snprintfz(name, 100, "unittest-%d-%ld-%ld", repeat, delay, shift); //debug_flags = 0xffffffff; - rrd_memory_mode = RRD_MEMORY_MODE_RAM; - rrd_update_every = 1; + default_rrd_memory_mode = RRD_MEMORY_MODE_RAM; + default_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; + RRDSET *st = rrdset_create_localhost("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1 + , RRDSET_TYPE_LINE); + rrdset_flag_set(st, RRDSET_FLAG_DEBUG); 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); + if(do_abs) rdabs = rrddim_add(st, "absolute", "absolute", 1, 1, RRD_ALGORITHM_ABSOLUTE); + if(do_inc) rdinc = rrddim_add(st, "incremental", "incremental", 1, 1, RRD_ALGORITHM_INCREMENTAL); + if(do_abst) rdabst = rrddim_add(st, "percentage-of-absolute-row", "percentage-of-absolute-row", 1, 1, RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL); + if(do_absi) rdabsi = rrddim_add(st, "percentage-of-incremental-row", "percentage-of-incremental-row", 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); long increment = 1000; collected_number i = 0; diff --git a/src/web_api_old.c b/src/web_api_old.c new file mode 100644 index 000000000..373e7e9f8 --- /dev/null +++ b/src/web_api_old.c @@ -0,0 +1,237 @@ +#include "common.h" + +int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type) { + if(!url || !*url) { + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Incomplete request."); + return 400; + } + + 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(host, tok); + if(!st) st = rrdset_find(host, 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 = (int)st->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 = str2i(tok); + if(lines < 1) lines = 1; + } + if(url) { + // parse the group count required + tok = mystrsep(&url, "/"); + if(tok && *tok) group_count = str2i(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 = str2ul(tok); + if(after < 0) after = 0; + } + if(url) { + // parse before time + tok = mystrsep(&url, "/"); + if(tok && *tok) before = str2ul(tok); + 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 = rrdset2json_api_old(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; +} + +inline int web_client_api_old_data_request_json(RRDHOST *host, struct web_client *w, char *url) { + return web_client_api_old_data_request(host, w, url, DATASOURCE_JSON); +} + +inline int web_client_api_old_data_request_jsonp(RRDHOST *host, struct web_client *w, char *url) { + return web_client_api_old_data_request(host, w, url, DATASOURCE_DATATABLE_JSONP); +} + +inline int web_client_api_old_graph_request(RRDHOST *host, struct web_client *w, char *url) { + // get the name of the data to show + char *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(host, tok); + if(!st) st = rrdset_find(host, 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); + } + st->last_accessed_time = now_realtime_sec(); + + 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_graph2json_api_old(st, url, w->response.data); + return 200; + } + + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "Graph name?\r\n"); + return 400; +} + +inline int web_client_api_old_list_request(RRDHOST *host, struct web_client *w, char *url) { + (void)url; + + buffer_flush(w->response.data); + RRDSET *st; + + rrdhost_rdlock(host); + rrdset_foreach_read(st, host) { + if(rrdset_is_available_for_viewers(st)) + buffer_sprintf(w->response.data, "%s\n", st->name); + } + rrdhost_unlock(host); + + return 200; +} + +inline int web_client_api_old_all_json(RRDHOST *host, struct web_client *w, char *url) { + (void)url; + + w->response.data->contenttype = CT_APPLICATION_JSON; + buffer_flush(w->response.data); + rrd_all2json_api_old(host, w->response.data); + return 200; +} diff --git a/src/web_api_old.h b/src/web_api_old.h new file mode 100644 index 000000000..dff48c2f3 --- /dev/null +++ b/src/web_api_old.h @@ -0,0 +1,13 @@ +#ifndef NETDATA_WEB_API_OLD_H +#define NETDATA_WEB_API_OLD_H + +#include "common.h" + +extern int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *url, int datasource_type); +extern int web_client_api_old_data_request_json(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_old_data_request_jsonp(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_old_graph_request(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_old_list_request(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_old_all_json(RRDHOST *host, struct web_client *w, char *url); + +#endif //NETDATA_WEB_API_OLD_H diff --git a/src/web_api_v1.c b/src/web_api_v1.c new file mode 100644 index 000000000..0acc43acb --- /dev/null +++ b/src/web_api_v1.c @@ -0,0 +1,903 @@ +#include "common.h" + +inline int web_client_api_request_v1_data_group(char *name, int def) { + 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, "incremental-sum")) + return GROUP_INCREMENTAL_SUM; + + return def; +} + +inline 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; + else if(!strcmp(tok, "unaligned")) + ret |= RRDR_OPTION_NOT_ALIGNED; + } + + return ret; +} + +inline uint32_t web_client_api_request_v1_data_format(char *name) { + 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_JSON)) // json + return DATASOURCE_JSON; + + 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_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_HTML)) // html + return DATASOURCE_HTML; + + 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_CSV_JSON_ARRAY)) // csvjsonarray + return DATASOURCE_CSV_JSON_ARRAY; + + return DATASOURCE_JSON; +} + +inline uint32_t web_client_api_request_v1_data_google_format(char *name) { + if(!strcmp(name, "json")) + return DATASOURCE_DATATABLE_JSONP; + + else if(!strcmp(name, "html")) + return DATASOURCE_HTML; + + else if(!strcmp(name, "csv")) + return DATASOURCE_CSV; + + else if(!strcmp(name, "tsv-excel")) + return DATASOURCE_TSV; + + return DATASOURCE_JSON; +} + + +inline int web_client_api_request_v1_alarms(RRDHOST *host, 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(host, w->response.data, all); + return 200; +} + +inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url) { + uint32_t after = 0; + + while(url) { + char *value = mystrsep(&url, "?&"); + if (!value || !*value) continue; + + char *name = mystrsep(&value, "="); + if(!name || !*name) continue; + if(!value || !*value) continue; + + if(!strcmp(name, "after")) after = (uint32_t)strtoul(value, NULL, 0); + } + + buffer_flush(w->response.data); + w->response.data->contenttype = CT_APPLICATION_JSON; + health_alarm_log2json(host, w->response.data, after); + return 200; +} + +inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)) { + 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(host, chart); + if(!st) st = rrdset_find_byname(host, chart); + if(!st) { + buffer_strcat(w->response.data, "Chart is not found: "); + buffer_strcat_htmlescape(w->response.data, chart); + ret = 404; + goto cleanup; + } + + w->response.data->contenttype = CT_APPLICATION_JSON; + st->last_accessed_time = now_realtime_sec(); + callback(st, w->response.data); + return 200; + + cleanup: + return ret; +} + +inline int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url) { + return web_client_api_request_single_chart(host, w, url, health_api_v1_chart_variables2json); +} + +inline int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url) { + (void)url; + + buffer_flush(w->response.data); + w->response.data->contenttype = CT_APPLICATION_JSON; + rrd_stats_api_v1_charts(host, w->response.data); + return 200; +} + +inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) { + int format = ALLMETRICS_SHELL; + + while(url) { + char *value = mystrsep(&url, "?&"); + if (!value || !*value) continue; + + char *name = mystrsep(&value, "="); + if(!name || !*name) continue; + if(!value || !*value) continue; + + if(!strcmp(name, "format")) { + if(!strcmp(value, ALLMETRICS_FORMAT_SHELL)) + format = ALLMETRICS_SHELL; + else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS)) + format = ALLMETRICS_PROMETHEUS; + else if(!strcmp(value, ALLMETRICS_FORMAT_JSON)) + format = ALLMETRICS_JSON; + else + format = 0; + } + } + + buffer_flush(w->response.data); + buffer_no_cacheable(w->response.data); + + switch(format) { + case ALLMETRICS_JSON: + w->response.data->contenttype = CT_APPLICATION_JSON; + rrd_stats_api_v1_charts_allmetrics_json(host, w->response.data); + return 200; + + case ALLMETRICS_SHELL: + w->response.data->contenttype = CT_TEXT_PLAIN; + rrd_stats_api_v1_charts_allmetrics_shell(host, w->response.data); + return 200; + + case ALLMETRICS_PROMETHEUS: + w->response.data->contenttype = CT_PROMETHEUS; + rrd_stats_api_v1_charts_allmetrics_prometheus(host, w->response.data); + return 200; + + default: + w->response.data->contenttype = CT_TEXT_PLAIN; + buffer_strcat(w->response.data, "Which format? '" ALLMETRICS_FORMAT_SHELL "', '" ALLMETRICS_FORMAT_PROMETHEUS "' and '" ALLMETRICS_FORMAT_JSON "' are currently supported."); + return 400; + } +} + +inline int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url) { + return web_client_api_request_single_chart(host, w, url, rrd_stats_api_v1_chart); +} + +int web_client_api_request_v1_badge(RRDHOST *host, 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_no_cacheable(w->response.data); + buffer_sprintf(w->response.data, "No chart id is given at the request."); + goto cleanup; + } + + RRDSET *st = rrdset_find(host, chart); + if(!st) st = rrdset_find_byname(host, chart); + if(!st) { + buffer_no_cacheable(w->response.data); + buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1); + ret = 200; + goto cleanup; + } + st->last_accessed_time = now_realtime_sec(); + + RRDCALC *rc = NULL; + if(alarm) { + rc = rrdcalc_find(st, alarm); + if (!rc) { + buffer_no_cacheable(w->response.data); + buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1); + ret = 200; + goto cleanup; + } + } + + long long multiply = (multiply_str && *multiply_str )?str2l(multiply_str):1; + long long divide = (divide_str && *divide_str )?str2l(divide_str):1; + long long before = (before_str && *before_str )?str2l(before_str):0; + long long after = (after_str && *after_str )?str2l(after_str):-st->update_every; + int points = (points_str && *points_str )?str2i(points_str):1; + int precision = (precision_str && *precision_str)?str2i(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 = (int)(before - after); + if(refresh < 0) refresh = -refresh; + } + } + else { + refresh = str2i(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) { + if (refresh > 0) { + buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh); + w->response.data->expires = now_realtime_sec() + refresh; + } + else buffer_no_cacheable(w->response.data); + + 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, + (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide, + units, + label_color, + value_color, + precision); + ret = 200; + } + else { + time_t latest_timestamp = 0; + int value_is_null = 1; + calculated_number n = NAN; + ret = 500; + + // if the collected value is too old, don't calculate its value + if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above))) + ret = rrdset2value_api_v1(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) { + buffer_no_cacheable(w->response.data); + value_is_null = 1; + n = 0; + ret = 200; + } + else if (refresh > 0) { + buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh); + w->response.data->expires = now_realtime_sec() + refresh; + } + else buffer_no_cacheable(w->response.data); + + // render the badge + buffer_svg(w->response.data, + label, + (value_is_null)?NAN:(n * multiply / divide), + units, + label_color, + value_color, + precision); + } + + cleanup: + buffer_free(dimensions); + return ret; +} + +// returns the HTTP code +inline int web_client_api_request_v1_data(RRDHOST *host, 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_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(host, chart); + if(!st) st = rrdset_find_byname(host, chart); + if(!st) { + buffer_strcat(w->response.data, "Chart is not found: "); + buffer_strcat_htmlescape(w->response.data, chart); + ret = 404; + goto cleanup; + } + st->last_accessed_time = now_realtime_sec(); + + long long before = (before_str && *before_str)?str2l(before_str):0; + long long after = (after_str && *after_str) ?str2l(after_str):0; + int points = (points_str && *points_str)?str2i(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 = rrdset2anything_api_v1(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: + buffer_free(dimensions); + return ret; +} + +inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url) { + 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[GUID_LEN + 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(respect_web_browser_do_not_track_policy && 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)) { + error("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"); + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "Invalid registry Access request."); + return 400; + } + else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) { + error("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"); + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "Invalid registry Delete request."); + return 400; + } + else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) { + error("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"); + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "Invalid registry Search request."); + return 400; + } + else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) { + error("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"); + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "Invalid registry Switch request."); + return 400; + } + + switch(action) { + case 'A': + w->tracking_required = 1; + return registry_request_access_json(host, w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec()); + + case 'D': + w->tracking_required = 1; + return registry_request_delete_json(host, w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec()); + + case 'S': + w->tracking_required = 1; + return registry_request_search_json(host, w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec()); + + case 'W': + w->tracking_required = 1; + return registry_request_switch_json(host, w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec()); + + case 'H': + return registry_request_hello_json(host, 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; + } +} + +inline int web_client_api_request_v1(RRDHOST *host, 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, hash_alarm_variables = 0, hash_raw = 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"); + hash_alarm_variables = simple_hash("alarm_variables"); + hash_raw = simple_hash("allmetrics"); + } + + // 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(host, w, url); + + else if(hash == hash_chart && !strcmp(tok, "chart")) + return web_client_api_request_v1_chart(host, w, url); + + else if(hash == hash_charts && !strcmp(tok, "charts")) + return web_client_api_request_v1_charts(host, w, url); + + else if(hash == hash_registry && !strcmp(tok, "registry")) + return web_client_api_request_v1_registry(host, w, url); + + else if(hash == hash_badge && !strcmp(tok, "badge.svg")) + return web_client_api_request_v1_badge(host, w, url); + + else if(hash == hash_alarms && !strcmp(tok, "alarms")) + return web_client_api_request_v1_alarms(host, w, url); + + else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log")) + return web_client_api_request_v1_alarm_log(host, w, url); + + else if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables")) + return web_client_api_request_v1_alarm_variables(host, w, url); + + else if(hash == hash_raw && !strcmp(tok, "allmetrics")) + return web_client_api_request_v1_allmetrics(host, w, url); + + else { + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "Unsupported v1 API command: "); + buffer_strcat_htmlescape(w->response.data, tok); + return 404; + } + } + else { + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Which API v1 command?"); + return 400; + } +} diff --git a/src/web_api_v1.h b/src/web_api_v1.h new file mode 100644 index 000000000..e980edb1d --- /dev/null +++ b/src/web_api_v1.h @@ -0,0 +1,21 @@ +#ifndef NETDATA_WEB_API_V1_H +#define NETDATA_WEB_API_V1_H + +extern int web_client_api_request_v1_data_group(char *name, int def); +extern uint32_t web_client_api_request_v1_data_options(char *o); +extern uint32_t web_client_api_request_v1_data_format(char *name); +extern uint32_t web_client_api_request_v1_data_google_format(char *name); + +extern int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)); +extern int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url); +extern int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url); + +#endif //NETDATA_WEB_API_V1_H diff --git a/src/web_buffer.c b/src/web_buffer.c index 6203db0f7..9f9ceda63 100644 --- a/src/web_buffer.c +++ b/src/web_buffer.c @@ -359,8 +359,9 @@ BUFFER *buffer_create(size_t size) return(b); } -void buffer_free(BUFFER *b) -{ +void buffer_free(BUFFER *b) { + if(unlikely(!b)) return; + buffer_overflow_check(b); debug(D_WEB_BUFFER, "Freeing web buffer of size %zu.", b->size); diff --git a/src/web_buffer_svg.c b/src/web_buffer_svg.c index cac365ab1..2591799d4 100644 --- a/src/web_buffer_svg.c +++ b/src/web_buffer_svg.c @@ -368,10 +368,226 @@ cleanup: 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 char *format_value_with_precision_and_unit(char *value_string, size_t value_string_len, calculated_number value, const char *units, int precision) { + if(unlikely(isnan(value) || isinf(value))) + value = 0.0; + + char *separator = ""; + if(unlikely(isalnum(*units))) + separator = " "; + + if(precision < 0) { + int len, lstop = 0, trim_zeros = 1; + + calculated_number abs = value; + if(isless(value, 0)) { + lstop = 1; + abs = -value; + } + + if(isgreaterequal(abs, 1000)) { + len = snprintfz(value_string, value_string_len, "%0.0Lf", (long double) value); + trim_zeros = 0; + } + else if(isgreaterequal(abs, 10)) len = snprintfz(value_string, value_string_len, "%0.1Lf", (long double) value); + else if(isgreaterequal(abs, 1)) len = snprintfz(value_string, value_string_len, "%0.2Lf", (long double) value); + else if(isgreaterequal(abs, 0.1)) len = snprintfz(value_string, value_string_len, "%0.2Lf", (long double) value); + else len = snprintfz(value_string, value_string_len, "%0.4Lf", (long double) value); + + if(unlikely(trim_zeros)) { + int l; + // remove trailing zeros from the decimal part + 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(unlikely(len <= 0)) len = 1; + snprintfz(&value_string[len], value_string_len - len, "%s%s", separator, units); + } + else { + if(precision > 50) precision = 50; + snprintfz(value_string, value_string_len, "%0.*Lf%s%s", precision, (long double) value, separator, units); + } + + return value_string; +} + +inline char *format_value_and_unit(char *value_string, size_t value_string_len, calculated_number value, const char *units, int precision) { + static uint32_t + hash_seconds = 0, + hash_seconds_ago = 0, + hash_minutes = 0, + hash_minutes_ago = 0, + hash_hours = 0, + hash_hours_ago = 0, + hash_onoff = 0, + hash_updown = 0, + hash_okerror = 0, + hash_okfailed = 0, + hash_empty = 0, + hash_null = 0, + hash_percentage = 0, + hash_percent = 0, + hash_pcent = 0; + + if(unlikely(!hash_seconds)) { + hash_seconds = simple_hash("seconds"); + hash_seconds_ago = simple_hash("seconds ago"); + hash_minutes = simple_hash("minutes"); + hash_minutes_ago = simple_hash("minutes ago"); + hash_hours = simple_hash("hours"); + hash_hours_ago = simple_hash("hours ago"); + hash_onoff = simple_hash("on/off"); + hash_updown = simple_hash("up/down"); + hash_okerror = simple_hash("ok/error"); + hash_okfailed = simple_hash("ok/failed"); + hash_empty = simple_hash("empty"); + hash_null = simple_hash("null"); + hash_percentage = simple_hash("percentage"); + hash_percent = simple_hash("percent"); + hash_pcent = simple_hash("pcent"); + } + + if(unlikely(!units)) units = ""; + + uint32_t hash_units = simple_hash(units); + + if(unlikely((hash_units == hash_seconds && !strcmp(units, "seconds")) || (hash_units == hash_seconds_ago && !strcmp(units, "seconds ago")))) { + if(value == 0.0) { + snprintfz(value_string, value_string_len, "%s", "now"); + return value_string; + } + else if(isnan(value) || isinf(value)) { + snprintfz(value_string, value_string_len, "%s", "never"); + return value_string; + } + + const char *suffix = (hash_units == hash_seconds_ago)?" ago":""; + + size_t s = (size_t)value; + size_t d = s / 86400; + s = s % 86400; + + size_t h = s / 3600; + s = s % 3600; + + size_t m = s / 60; + s = s % 60; + + if(d) + snprintfz(value_string, value_string_len, "%zu %s %02zu:%02zu:%02zu%s", d, (d == 1)?"day":"days", h, m, s, suffix); + else + snprintfz(value_string, value_string_len, "%02zu:%02zu:%02zu%s", h, m, s, suffix); + + return value_string; + } + + else if(unlikely((hash_units == hash_minutes && !strcmp(units, "minutes")) || (hash_units == hash_minutes_ago && !strcmp(units, "minutes ago")))) { + if(value == 0.0) { + snprintfz(value_string, value_string_len, "%s", "now"); + return value_string; + } + else if(isnan(value) || isinf(value)) { + snprintfz(value_string, value_string_len, "%s", "never"); + return value_string; + } + + const char *suffix = (hash_units == hash_minutes_ago)?" ago":""; + + size_t m = (size_t)value; + size_t d = m / (60 * 24); + m = m % (60 * 24); + + size_t h = m / 60; + m = m % 60; + + if(d) + snprintfz(value_string, value_string_len, "%zud %02zuh %02zum%s", d, h, m, suffix); + else + snprintfz(value_string, value_string_len, "%zuh %zum%s", h, m, suffix); + + return value_string; + } + + else if(unlikely((hash_units == hash_hours && !strcmp(units, "hours")) || (hash_units == hash_hours_ago && !strcmp(units, "hours ago")))) { + if(value == 0.0) { + snprintfz(value_string, value_string_len, "%s", "now"); + return value_string; + } + else if(isnan(value) || isinf(value)) { + snprintfz(value_string, value_string_len, "%s", "never"); + return value_string; + } + + const char *suffix = (hash_units == hash_hours_ago)?" ago":""; + + size_t h = (size_t)value; + size_t d = h / 24; + h = h % 24; + + if(d) + snprintfz(value_string, value_string_len, "%zud %zuh%s", d, h, suffix); + else + snprintfz(value_string, value_string_len, "%zuh%s", h, suffix); + + return value_string; + } + + else if(unlikely(hash_units == hash_onoff && !strcmp(units, "on/off"))) { + snprintfz(value_string, value_string_len, "%s", (value != 0.0)?"on":"off"); + return value_string; + } + + else if(unlikely(hash_units == hash_updown && !strcmp(units, "up/down"))) { + snprintfz(value_string, value_string_len, "%s", (value != 0.0)?"up":"down"); + return value_string; + } + + else if(unlikely(hash_units == hash_okerror && !strcmp(units, "ok/error"))) { + snprintfz(value_string, value_string_len, "%s", (value != 0.0)?"ok":"error"); + return value_string; + } + + else if(unlikely(hash_units == hash_okfailed && !strcmp(units, "ok/failed"))) { + snprintfz(value_string, value_string_len, "%s", (value != 0.0)?"ok":"failed"); + return value_string; + } + + else if(unlikely(hash_units == hash_empty && !strcmp(units, "empty"))) + units = ""; + + else if(unlikely(hash_units == hash_null && !strcmp(units, "null"))) + units = ""; + + else if(unlikely(hash_units == hash_percentage && !strcmp(units, "percentage"))) + units = "%"; + + else if(unlikely(hash_units == hash_percent && !strcmp(units, "percent"))) + units = "%"; + + else if(unlikely(hash_units == hash_pcent && !strcmp(units, "pcent"))) + units = "%"; + + + if(unlikely(isnan(value) || isinf(value))) { + strcpy(value_string, "-"); + return value_string; + } + + return format_value_with_precision_and_unit(value_string, value_string_len, value, units, precision); } static inline const char *color_map(const char *color) { @@ -391,7 +607,13 @@ static inline const char *color_map(const char *color) { return color; } -static inline void calc_colorz(const char *color, char *final, size_t len, calculated_number value, int value_is_null) { +static inline void calc_colorz(const char *color, char *final, size_t len, calculated_number value) { + int value_is_null = 0; + if(isnan(value) || isinf(value)) { + value = 0.0; + value_is_null = 1; + } + char color_buffer[256 + 1] = ""; char value_buffer[256 + 1] = ""; char comparison = '>'; @@ -501,7 +723,7 @@ static inline void calc_colorz(const char *color, char *final, size_t len, calcu // 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) { +void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision) { char label_buffer[LABEL_STRING_SIZE + 1] , value_color_buffer[COLOR_STRING_SIZE + 1] , value_string[VALUE_STRING_SIZE + 1] @@ -516,102 +738,10 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch 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(!strcmp(units, "seconds"))) { - size_t s = (size_t)value; - size_t d = s / 86400; - s = s % 86400; - - size_t h = s / 3600; - s = s % 3600; - - size_t m = s / 60; - s = s % 60; - - if(d) - snprintfz(value_string, VALUE_STRING_SIZE, "%zu %s %02zu:%02zu:%02zu", d, (d == 1)?"day":"days", h, m, s); - else - snprintfz(value_string, VALUE_STRING_SIZE, "%02zu:%02zu:%02zu", h, m, s); - } - - else if(unlikely(!strcmp(units, "minutes"))) { - size_t m = (size_t)value; - size_t d = m / (60 * 24); - m = m % (60 * 24); - - size_t h = m / 60; - m = m % 60; + value_color = (isnan(value) || isinf(value))?"#999":"#4c1"; - if(d) - snprintfz(value_string, VALUE_STRING_SIZE, "%zud %02zuh %02zum", d, h, m); - else - snprintfz(value_string, VALUE_STRING_SIZE, "%zuh %zum", h, m); - } - - else if(unlikely(!strcmp(units, "hours"))) { - size_t h = (size_t)value; - size_t d = h / 24; - h = h % 24; - - if(d) - snprintfz(value_string, VALUE_STRING_SIZE, "%zud %zuh", d, h); - else - snprintfz(value_string, VALUE_STRING_SIZE, "%zuh", h); - } - - else if(unlikely(value_is_null)) - strcpy(value_string, "-"); - - else if(precision < 0) { - int len, lstop = 0, trim_zeros = 1; - - 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); trim_zeros = 0; } - 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); - - if(unlikely(trim_zeros)) { - int l; - // remove trailing zeros from the decimal part - 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); - } + calc_colorz(value_color, value_color_buffer, COLOR_STRING_SIZE, value); + format_value_and_unit(value_string, VALUE_STRING_SIZE, value, units, precision); // we need to copy the label, since verdana11_width may write to it strncpyz(label_buffer, label, LABEL_STRING_SIZE); diff --git a/src/web_buffer_svg.h b/src/web_buffer_svg.h index 1281847eb..49f73e445 100644 --- a/src/web_buffer_svg.h +++ b/src/web_buffer_svg.h @@ -1,6 +1,7 @@ #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); +extern void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision); +extern char *format_value_and_unit(char *value_string, size_t value_string_len, calculated_number value, const char *units, int precision); #endif /* NETDATA_WEB_BUFFER_SVG_H */ diff --git a/src/web_client.c b/src/web_client.c index 4b6ccf646..b5b25899f 100644 --- a/src/web_client.c +++ b/src/web_client.c @@ -5,7 +5,8 @@ #define TOO_BIG_REQUEST 16384 int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS; -int web_donotrack_comply = 0; +int respect_web_browser_do_not_track_policy = 0; +char *web_x_frame_options = NULL; #ifdef NETDATA_WITH_ZLIB int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRATEGY; @@ -24,6 +25,8 @@ static inline int web_client_crock_socket(struct web_client *w) { return -1; } } +#else + (void)w; #endif /* TCP_CORK */ return 0; @@ -39,13 +42,14 @@ static inline int web_client_uncrock_socket(struct web_client *w) { return -1; } } +#else + (void)w; #endif /* TCP_CORK */ return 0; } -struct web_client *web_client_create(int listener) -{ +struct web_client *web_client_create(int listener) { struct web_client *w; w = callocz(1, sizeof(struct web_client)); @@ -222,9 +226,9 @@ struct web_client *web_client_free(struct web_client *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); + buffer_free(w->response.header_output); + buffer_free(w->response.header); + 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); @@ -239,7 +243,7 @@ uid_t web_files_uid(void) { static uid_t owner_uid = 0; if(unlikely(!web_owner)) { - web_owner = config_get("global", "web files owner", config_get("global", "run as user", "")); + web_owner = config_get(CONFIG_SECTION_WEB, "web files owner", config_get(CONFIG_SECTION_GLOBAL, "run as user", "")); if(!web_owner || !*web_owner) owner_uid = geteuid(); else { @@ -266,7 +270,7 @@ gid_t web_files_gid(void) { static gid_t owner_gid = 0; if(unlikely(!web_group)) { - web_group = config_get("global", "web files group", config_get("global", "web files owner", "")); + web_group = config_get(CONFIG_SECTION_WEB, "web files group", config_get(CONFIG_SECTION_WEB, "web files owner", "")); if(!web_group || !*web_group) owner_gid = getegid(); else { @@ -288,14 +292,8 @@ gid_t web_files_gid(void) { 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); +int mysendfile(struct web_client *w, char *filename) { + debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename); // skip leading slashes while (*filename == '/') filename++; @@ -308,6 +306,7 @@ int mysendfile(struct web_client *w, char *filename) 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); + w->response.data->contenttype = CT_TEXT_HTML; buffer_sprintf(w->response.data, "Filename contains invalid characters: "); buffer_strcat_htmlescape(w->response.data, filename); return 400; @@ -317,6 +316,7 @@ int mysendfile(struct web_client *w, char *filename) // 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); + w->response.data->contenttype = CT_TEXT_HTML; buffer_strcat(w->response.data, "Relative filenames are not supported: "); buffer_strcat_htmlescape(w->response.data, filename); return 400; @@ -324,12 +324,13 @@ int mysendfile(struct web_client *w, char *filename) // access the file char webfilename[FILENAME_MAX + 1]; - snprintfz(webfilename, FILENAME_MAX, "%s/%s", web_dir, filename); + snprintfz(webfilename, FILENAME_MAX, "%s/%s", netdata_configured_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); + w->response.data->contenttype = CT_TEXT_HTML; buffer_strcat(w->response.data, "File does not exist, or is not accessible: "); buffer_strcat_htmlescape(w->response.data, webfilename); return 404; @@ -338,6 +339,7 @@ int mysendfile(struct web_client *w, char *filename) // 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()); + w->response.data->contenttype = CT_TEXT_HTML; buffer_strcat(w->response.data, "Access to file is not permitted: "); buffer_strcat_htmlescape(w->response.data, webfilename); return 403; @@ -346,6 +348,7 @@ int mysendfile(struct web_client *w, char *filename) // 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()); + w->response.data->contenttype = CT_TEXT_HTML; buffer_strcat(w->response.data, "Access to file is not permitted: "); buffer_strcat_htmlescape(w->response.data, webfilename); return 403; @@ -358,6 +361,7 @@ int mysendfile(struct web_client *w, char *filename) if((stat.st_mode & S_IFMT) != S_IFREG) { error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename); + w->response.data->contenttype = CT_TEXT_HTML; buffer_strcat(w->response.data, "Access to file is not permitted: "); buffer_strcat_htmlescape(w->response.data, webfilename); return 403; @@ -370,6 +374,7 @@ int mysendfile(struct web_client *w, char *filename) if(errno == EBUSY || errno == EAGAIN) { error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename); + w->response.data->contenttype = CT_TEXT_HTML; buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename); buffer_strcat(w->response.data, "File is currently busy, please try again later: "); buffer_strcat_htmlescape(w->response.data, webfilename); @@ -377,6 +382,7 @@ int mysendfile(struct web_client *w, char *filename) } else { error("%llu: Cannot open file '%s'.", w->id, webfilename); + w->response.data->contenttype = CT_TEXT_HTML; buffer_strcat(w->response.data, "Cannot open file: "); buffer_strcat_htmlescape(w->response.data, webfilename); return 404; @@ -536,98 +542,6 @@ void buffer_data_options2string(BUFFER *wb, uint32_t options) { } } -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; - 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; - - 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_JSONP)) // jsonp - return DATASOURCE_JSONP; - - 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_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_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_CSV_JSON_ARRAY)) // csvjsonarray - return DATASOURCE_CSV_JSON_ARRAY; - - return DATASOURCE_JSON; -} - -uint32_t web_client_api_request_v1_data_google_format(char *name) -{ - if(!strcmp(name, "json")) - return DATASOURCE_DATATABLE_JSONP; - - else if(!strcmp(name, "html")) - return DATASOURCE_HTML; - - else if(!strcmp(name, "csv")) - return DATASOURCE_CSV; - - else if(!strcmp(name, "tsv-excel")) - return DATASOURCE_TSV; - - return DATASOURCE_JSON; -} - const char *group_method2string(int group) { switch(group) { case GROUP_UNDEFINED: @@ -653,844 +567,27 @@ const char *group_method2string(int group) { } } -int web_client_api_request_v1_data_group(char *name, int def) -{ - 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, "incremental-sum")) - return GROUP_INCREMENTAL_SUM; - - 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) -{ - uint32_t after = 0; - - while(url) { - char *value = mystrsep(&url, "?&"); - if (!value || !*value) continue; - - char *name = mystrsep(&value, "="); - if(!name || !*name) continue; - if(!value || !*value) continue; - - if(!strcmp(name, "after")) after = strtoul(value, NULL, 0); - } - - buffer_flush(w->response.data); - w->response.data->contenttype = CT_APPLICATION_JSON; - health_alarm_log2json(&localhost, w->response.data, after); - return 200; -} - -int web_client_api_request_single_chart(struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf)) -{ - 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_strcat(w->response.data, "Chart is not found: "); - buffer_strcat_htmlescape(w->response.data, chart); - ret = 404; - goto cleanup; - } - - w->response.data->contenttype = CT_APPLICATION_JSON; - callback(st, w->response.data); - return 200; - - cleanup: - return ret; -} - -int web_client_api_request_v1_alarm_variables(struct web_client *w, char *url) -{ - return web_client_api_request_single_chart(w, url, health_api_v1_chart_variables2json); -} - -int web_client_api_request_v1_charts(struct web_client *w, char *url) -{ - (void)url; - - 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_allmetrics(struct web_client *w, char *url) -{ - int format = ALLMETRICS_SHELL; - - while(url) { - char *value = mystrsep(&url, "?&"); - if (!value || !*value) continue; - - char *name = mystrsep(&value, "="); - if(!name || !*name) continue; - if(!value || !*value) continue; - - if(!strcmp(name, "format")) { - if(!strcmp(value, ALLMETRICS_FORMAT_SHELL)) - format = ALLMETRICS_SHELL; - else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS)) - format = ALLMETRICS_PROMETHEUS; - else - format = 0; - } - } - - buffer_flush(w->response.data); - buffer_no_cacheable(w->response.data); - - switch(format) { - case ALLMETRICS_SHELL: - w->response.data->contenttype = CT_TEXT_PLAIN; - rrd_stats_api_v1_charts_allmetrics_shell(w->response.data); - return 200; - - case ALLMETRICS_PROMETHEUS: - w->response.data->contenttype = CT_PROMETHEUS; - rrd_stats_api_v1_charts_allmetrics_prometheus(w->response.data); - return 200; - - default: - w->response.data->contenttype = CT_TEXT_PLAIN; - buffer_strcat(w->response.data, "Which format? Only '" ALLMETRICS_FORMAT_SHELL "' and '" ALLMETRICS_FORMAT_PROMETHEUS "' is currently supported."); - return 400; - } -} - -int web_client_api_request_v1_chart(struct web_client *w, char *url) -{ - return web_client_api_request_single_chart(w, url, rrd_stats_api_v1_chart); -} - -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_no_cacheable(w->response.data); - 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_no_cacheable(w->response.data); - 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_no_cacheable(w->response.data); - buffer_svg(w->response.data, "alarm not found", 0, "", NULL, NULL, 1, -1); - ret = 200; - goto cleanup; - } - } - - long long multiply = (multiply_str && *multiply_str )?str2l(multiply_str):1; - long long divide = (divide_str && *divide_str )?str2l(divide_str):1; - long long before = (before_str && *before_str )?str2l(before_str):0; - long long after = (after_str && *after_str )?str2l(after_str):-st->update_every; - int points = (points_str && *points_str )?str2i(points_str):1; - int precision = (precision_str && *precision_str)?str2i(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 = (int)(before - after); - if(refresh < 0) refresh = -refresh; - } - } - else { - refresh = str2i(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); - w->response.data->expires = now_realtime_sec() + refresh; - } - else buffer_no_cacheable(w->response.data); - - 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) >= (now_realtime_sec() - (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) { - buffer_no_cacheable(w->response.data); - value_is_null = 1; - n = 0; - ret = 200; - } - else if (refresh > 0) { - buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh); - w->response.data->expires = now_realtime_sec() + refresh; - } - else buffer_no_cacheable(w->response.data); - - // 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_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_strcat(w->response.data, "Chart is not found: "); - buffer_strcat_htmlescape(w->response.data, chart); - ret = 404; - goto cleanup; - } - - long long before = (before_str && *before_str)?str2l(before_str):0; - long long after = (after_str && *after_str) ?str2l(after_str):0; - int points = (points_str && *points_str)?str2i(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; -} - - -int web_client_api_request_v1_registry(struct web_client *w, char *url) -{ - 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[GUID_LEN + 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) { +static inline int check_host_and_call(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) { + if(unlikely(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) { 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."); + buffer_strcat(w->response.data, "This host does not maintain a database"); return 400; } - if(action == 'A' && (!machine_guid || !machine_url || !url_name)) { - error("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"); - buffer_flush(w->response.data); - buffer_strcat(w->response.data, "Invalid registry Access request."); - return 400; - } - else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) { - error("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"); - buffer_flush(w->response.data); - buffer_strcat(w->response.data, "Invalid registry Delete request."); - return 400; - } - else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) { - error("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"); - buffer_flush(w->response.data); - buffer_strcat(w->response.data, "Invalid registry Search request."); - return 400; - } - else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) { - error("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"); - buffer_flush(w->response.data); - buffer_strcat(w->response.data, "Invalid registry Switch request."); - return 400; - } - - switch(action) { - case 'A': - w->tracking_required = 1; - return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, now_realtime_sec()); - - case 'D': - w->tracking_required = 1; - return registry_request_delete_json(w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec()); - - case 'S': - w->tracking_required = 1; - return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec()); - - case 'W': - w->tracking_required = 1; - return registry_request_switch_json(w, person_guid, machine_guid, machine_url, to_person_guid, now_realtime_sec()); - - 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; - } + return func(host, w, url); } -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, hash_alarm_variables = 0, hash_raw = 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"); - hash_alarm_variables = simple_hash("alarm_variables"); - hash_raw = simple_hash("allmetrics"); - } - - // 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 if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables")) - return web_client_api_request_v1_alarm_variables(w, url); - - else if(hash == hash_raw && !strcmp(tok, "allmetrics")) - return web_client_api_request_v1_allmetrics(w, url); - - else { - buffer_flush(w->response.data); - buffer_strcat(w->response.data, "Unsupported v1 API command: "); - buffer_strcat_htmlescape(w->response.data, tok); - return 404; - } - } - else { - buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Which API v1 command?"); - return 400; - } -} - -int web_client_api_request(struct web_client *w, char *url) +int web_client_api_request(RRDHOST *host, 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); + return web_client_api_request_v1(host, w, url); else { buffer_flush(w->response.data); + w->response.data->contenttype = CT_TEXT_HTML; buffer_strcat(w->response.data, "Unsupported API version: "); buffer_strcat_htmlescape(w->response.data, tok); return 404; @@ -1503,179 +600,6 @@ int web_client_api_request(struct web_client *w, char *url) } } -int web_client_api_old_data_request(struct web_client *w, char *url, int datasource_type) -{ - if(!url || !*url) { - buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Incomplete request."); - return 400; - } - - 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 = str2i(tok); - if(lines < 1) lines = 1; - } - if(url) { - // parse the group count required - tok = mystrsep(&url, "/"); - if(tok && *tok) group_count = str2i(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 = str2ul(tok); - if(after < 0) after = 0; - } - if(url) { - // parse before time - tok = mystrsep(&url, "/"); - if(tok && *tok) before = str2ul(tok); - 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; -} - const char *web_content_type_to_string(uint8_t contenttype) { switch(contenttype) { case CT_TEXT_HTML: @@ -1834,7 +758,7 @@ static inline char *http_header_parse(struct web_client *w, char *s) { if(strcasestr(v, "keep-alive")) w->keepalive = 1; } - else if(web_donotrack_comply && hash == hash_donottrack && !strcasecmp(s, "DNT")) { + else if(respect_web_browser_do_not_track_policy && hash == hash_donottrack && !strcasecmp(s, "DNT")) { if(*v == '0') w->donottrack = 0; else if(*v == '1') w->donottrack = 1; } @@ -1862,7 +786,13 @@ static inline char *http_header_parse(struct web_client *w, char *s) { // > 0 : request is not supported // < 0 : request is incomplete - wait for more data -static inline int http_request_validate(struct web_client *w) { +typedef enum http_validation { + HTTP_VALIDATION_OK, + HTTP_VALIDATION_NOT_SUPPORTED, + HTTP_VALIDATION_INCOMPLETE +} HTTP_VALIDATION; + +static inline HTTP_VALIDATION http_request_validate(struct web_client *w) { char *s = w->response.data->buffer, *encoded_url = NULL; // is is a valid request? @@ -1874,9 +804,13 @@ static inline int http_request_validate(struct web_client *w) { encoded_url = s = &s[8]; w->mode = WEB_CLIENT_MODE_OPTIONS; } + else if(!strncmp(s, "STREAM ", 7)) { + encoded_url = s = &s[7]; + w->mode = WEB_CLIENT_MODE_STREAM; + } else { w->wait_receive = 0; - return 1; + return HTTP_VALIDATION_NOT_SUPPORTED; } // find the SPACE + "HTTP/" @@ -1892,7 +826,7 @@ static inline int http_request_validate(struct web_client *w) { // incomplete requests if(unlikely(!*s)) { w->wait_receive = 1; - return -2; + return HTTP_VALIDATION_INCOMPLETE; } // we have the end of encoded_url - remember it @@ -1923,7 +857,7 @@ static inline int http_request_validate(struct web_client *w) { strncpyz(w->last_url, w->decoded_url, URL_MAX); w->wait_receive = 0; - return 0; + return HTTP_VALIDATION_OK; } // another header line @@ -1933,259 +867,26 @@ static inline int http_request_validate(struct web_client *w) { // incomplete request w->wait_receive = 1; - return -3; + return HTTP_VALIDATION_INCOMPLETE; } -void web_client_process(struct web_client *w) { - 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; - -#ifdef NETDATA_INTERNAL_CHECKS - static uint32_t hash_exit = 0, hash_debug = 0, hash_mirror = 0; -#endif - - // start timing us - now_realtime_timeval(&w->tv_in); - - 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"); -#ifdef NETDATA_INTERNAL_CHECKS - hash_exit = simple_hash("exit"); - hash_debug = simple_hash("debug"); - hash_mirror = simple_hash("mirror"); -#endif - } - - 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(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); - } - 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_strcat(w->response.data, "Chart is not found: "); - buffer_strcat_htmlescape(w->response.data, 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 has now debug %s: ", st->debug?"enabled":"disabled"); - buffer_strcat_htmlescape(w->response.data, tok); - 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:"/"); - } - } - } - - now_realtime_timeval(&w->tv_ready); - w->response.sent = 0; - w->response.code = code; - - // set a proper last modified date - if(unlikely(!w->response.data->date)) - w->response.data->date = w->tv_ready.tv_sec; - - if(unlikely(code != 200)) +static inline void web_client_send_http_header(struct web_client *w) { + if(unlikely(w->response.code != 200)) buffer_no_cacheable(w->response.data); // set a proper expiration date, if not already set if(unlikely(!w->response.data->expires)) { if(w->response.data->options & WB_CONTENT_NO_CACHEABLE) - w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every; + w->response.data->expires = w->tv_ready.tv_sec + localhost->rrd_update_every; else w->response.data->expires = w->tv_ready.tv_sec + 86400; } // prepare the HTTP response header - debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code); + debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, w->response.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); + const char *code_msg = web_response_code_to_string(w->response.code); // prepare the last modified and expiration dates char date[32], edate[32]; @@ -2200,61 +901,64 @@ void web_client_process(struct web_client *w) { } 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 - ); + "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" + , w->response.code, code_msg + , w->keepalive?"keep-alive":"close" + , w->origin + , content_type_string + , date + ); + + if(unlikely(web_x_frame_options)) + buffer_sprintf(w->response.header_output, "X-Frame-Options: %s\r\n", web_x_frame_options); if(w->cookie1[0] || w->cookie2[0]) { if(w->cookie1[0]) { buffer_sprintf(w->response.header_output, - "Set-Cookie: %s\r\n", - w->cookie1); + "Set-Cookie: %s\r\n", + w->cookie1); } if(w->cookie2[0]) { buffer_sprintf(w->response.header_output, - "Set-Cookie: %s\r\n", - w->cookie2); + "Set-Cookie: %s\r\n", + w->cookie2); } - if(web_donotrack_comply) + if(respect_web_browser_do_not_track_policy) buffer_sprintf(w->response.header_output, - "Tk: T;cookies\r\n"); + "Tk: T;cookies\r\n"); } else { - if(web_donotrack_comply) { + if(respect_web_browser_do_not_track_policy) { if(w->tracking_required) buffer_sprintf(w->response.header_output, - "Tk: T;cookies\r\n"); + "Tk: T;cookies\r\n"); else buffer_sprintf(w->response.header_output, - "Tk: N\r\n"); + "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, pragma, cache-control\r\n" - "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14 - ); + "Access-Control-Allow-Methods: GET, OPTIONS\r\n" + "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n" + "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14 + ); } else { buffer_sprintf(w->response.header_output, - "Cache-Control: %s\r\n" - "Expires: %s\r\n", - (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public", - edate); + "Cache-Control: %s\r\n" + "Expires: %s\r\n", + (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public", + edate); } // copy a possibly available custom header @@ -2264,9 +968,9 @@ void web_client_process(struct web_client *w) { // headers related to the transfer method if(likely(w->response.zoutput)) { buffer_strcat(w->response.header_output, - "Content-Encoding: gzip\r\n" - "Transfer-Encoding: chunked\r\n" - ); + "Content-Encoding: gzip\r\n" + "Transfer-Encoding: chunked\r\n" + ); } else { if(likely((w->response.data->len || w->response.rlen))) { @@ -2284,35 +988,284 @@ void web_client_process(struct web_client *w) { // 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) - ); + , 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); + ssize_t 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); + , w->id + , buffer_strlen(w->response.header_output) + , bytes); WEB_CLIENT_IS_DEAD(w); return; } - else + else w->stats_sent_bytes += bytes; +} + +static inline int web_client_process_url(RRDHOST *host, struct web_client *w, char *url); + +static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, char *url) { + static uint32_t hash_localhost = 0; + + if(unlikely(!hash_localhost)) { + hash_localhost = simple_hash("localhost"); + } + + if(host != localhost) { + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "Nesting of hosts is not allowed."); + return 400; + } + + char *tok = mystrsep(&url, "/?&"); + if(tok && *tok) { + debug(D_WEB_CLIENT, "%llu: Searching for host with name '%s'.", w->id, tok); + + // copy the URL, we need it to serve files + w->last_url[0] = '/'; + if(url && *url) strncpyz(&w->last_url[1], url, URL_MAX - 1); + else w->last_url[1] = '\0'; + + uint32_t hash = simple_hash(tok); + + host = rrdhost_find_by_hostname(tok, hash); + if(!host) host = rrdhost_find_by_guid(tok, hash); + + if(host) return web_client_process_url(host, w, url); + } + + buffer_flush(w->response.data); + w->response.data->contenttype = CT_TEXT_HTML; + buffer_strcat(w->response.data, "This netdata does not maintain a database for host: "); + buffer_strcat_htmlescape(w->response.data, tok?tok:""); + return 404; +} + +static inline int web_client_process_url(RRDHOST *host, struct web_client *w, char *url) { + 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_host = 0; + +#ifdef NETDATA_INTERNAL_CHECKS + static uint32_t hash_exit = 0, hash_debug = 0, hash_mirror = 0; +#endif + + 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_host = simple_hash("host"); +#ifdef NETDATA_INTERNAL_CHECKS + hash_exit = simple_hash("exit"); + hash_debug = simple_hash("debug"); + hash_mirror = simple_hash("mirror"); +#endif + } + + char *tok = mystrsep(&url, "/?"); + if(likely(tok && *tok)) { + uint32_t hash = simple_hash(tok); + debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok); + + if(unlikely(hash == hash_api && strcmp(tok, "api") == 0)) { // current API + debug(D_WEB_CLIENT_ACCESS, "%llu: API request ...", w->id); + return check_host_and_call(host, w, url, web_client_api_request); + } + else if(unlikely(hash == hash_host && strcmp(tok, "host") == 0)) { // host switching + debug(D_WEB_CLIENT_ACCESS, "%llu: host switch request ...", w->id); + return web_client_switch_host(host, w, url); + } + else if(unlikely(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0)) { // old API "data" + debug(D_WEB_CLIENT_ACCESS, "%llu: old API data request...", w->id); + return check_host_and_call(host, w, url, web_client_api_old_data_request_json); + } + else if(unlikely(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0)) { // old API "datasource" + debug(D_WEB_CLIENT_ACCESS, "%llu: old API datasource request...", w->id); + return check_host_and_call(host, w, url, web_client_api_old_data_request_jsonp); + } + else if(unlikely(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0)) { // old API "graph" + debug(D_WEB_CLIENT_ACCESS, "%llu: old API graph request...", w->id); + return check_host_and_call(host, w, url, web_client_api_old_graph_request); + } + else if(unlikely(hash == hash_list && strcmp(tok, "list") == 0)) { // old API "list" + debug(D_WEB_CLIENT_ACCESS, "%llu: old API list request...", w->id); + return check_host_and_call(host, w, url, web_client_api_old_list_request); + } + else if(unlikely(hash == hash_all_json && strcmp(tok, "all.json") == 0)) { // old API "all.json" + debug(D_WEB_CLIENT_ACCESS, "%llu: old API all.json request...", w->id); + return check_host_and_call(host, w, url, web_client_api_old_all_json); + } + else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) { // netdata.conf + debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id); + w->response.data->contenttype = CT_TEXT_PLAIN; + buffer_flush(w->response.data); + config_generate(w->response.data, 0); + return 200; + } +#ifdef NETDATA_INTERNAL_CHECKS + else if(unlikely(hash == hash_exit && strcmp(tok, "exit") == 0)) { + 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); + return 200; + } + else if(unlikely(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(host, tok); + if(!st) st = rrdset_find(host, tok); + if(!st) { + w->response.data->contenttype = CT_TEXT_HTML; + buffer_strcat(w->response.data, "Chart is not found: "); + buffer_strcat_htmlescape(w->response.data, tok); + debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok); + return 404; + } + + debug_flags |= D_RRD_STATS; + + if(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)) + rrdset_flag_clear(st, RRDSET_FLAG_DEBUG); + else + rrdset_flag_set(st, RRDSET_FLAG_DEBUG); + + w->response.data->contenttype = CT_TEXT_HTML; + buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled"); + buffer_strcat_htmlescape(w->response.data, tok); + debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled"); + return 200; + } + + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "debug which chart?\r\n"); + return 400; + } + else if(unlikely(hash == hash_mirror && strcmp(tok, "mirror") == 0)) { + 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 + + return 200; + } +#endif /* NETDATA_INTERNAL_CHECKS */ + } + + char filename[FILENAME_MAX+1]; + url = filename; + strncpyz(filename, w->last_url, FILENAME_MAX); + tok = mystrsep(&url, "?"); + buffer_flush(w->response.data); + return mysendfile(w, (tok && *tok)?tok:"/"); +} + +void web_client_process_request(struct web_client *w) { + + // start timing us + now_realtime_timeval(&w->tv_in); + + switch(http_request_validate(w)) { + case HTTP_VALIDATION_OK: + switch(w->mode) { + case WEB_CLIENT_MODE_STREAM: + w->response.code = rrdpush_receiver_thread_spawn(localhost, w, w->decoded_url); + return; + + case WEB_CLIENT_MODE_OPTIONS: + w->response.data->contenttype = CT_TEXT_PLAIN; + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "OK"); + w->response.code = 200; + break; + + case WEB_CLIENT_MODE_FILECOPY: + case WEB_CLIENT_MODE_NORMAL: + w->response.code = web_client_process_url(localhost, w, w->decoded_url); + break; + } + break; + + case HTTP_VALIDATION_INCOMPLETE: + 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); + + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Received request is too big (%zu bytes).\r\n", w->response.data->len); + w->response.code = 400; + } + else { + // wait for more data + return; + } + break; + + case HTTP_VALIDATION_NOT_SUPPORTED: + debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer); + + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "I don't understand you...\r\n"); + w->response.code = 400; + break; + } + + // keep track of the time we done processing + now_realtime_timeval(&w->tv_ready); + + w->response.sent = 0; + + // set a proper last modified date + if(unlikely(!w->response.data->date)) + w->response.data->date = w->tv_ready.tv_sec; + + web_client_send_http_header(w); // 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_STREAM: + debug(D_WEB_CLIENT, "%llu: STREAM done.", w->id); + break; + 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; @@ -2344,7 +1297,7 @@ void web_client_process(struct web_client *w) { break; default: - fatal("%llu: Unknown client mode %d.", w->id, w->mode); + fatal("%llu: Unknown client mode %u.", w->id, w->mode); break; } } @@ -2666,11 +1619,14 @@ void *web_client_main(void *ptr) struct web_client *w = ptr; struct pollfd fds[2], *ifd, *ofd; - int retval, fdmax = 0, timeout; + int retval, timeout; + nfds_t fdmax = 0; log_access("%llu: %s port %s connected on thread task id %d", w->id, w->client_ip, w->client_port, gettid()); for(;;) { + if(unlikely(netdata_exit)) break; + if(unlikely(w->dead)) { debug(D_WEB_CLIENT, "%llu: client is dead.", w->id); break; @@ -2722,6 +1678,8 @@ void *web_client_main(void *ptr) timeout = web_client_timeout * 1000; retval = poll(fds, fdmax, timeout); + if(unlikely(netdata_exit)) break; + if(unlikely(retval == -1)) { if(errno == EAGAIN || errno == EINTR) { debug(D_WEB_CLIENT, "%llu: EAGAIN received.", w->id); @@ -2736,6 +1694,8 @@ void *web_client_main(void *ptr) break; } + if(unlikely(netdata_exit)) break; + int used = 0; if(w->wait_send && ofd->revents & POLLOUT) { used++; @@ -2745,6 +1705,8 @@ void *web_client_main(void *ptr) } } + if(unlikely(netdata_exit)) break; + if(w->wait_receive && (ifd->revents & POLLIN || ifd->revents & POLLPRI)) { used++; if(web_client_receive(w) < 0) { @@ -2754,7 +1716,12 @@ void *web_client_main(void *ptr) if(w->mode == WEB_CLIENT_MODE_NORMAL) { debug(D_WEB_CLIENT, "%llu: Attempting to process received data.", w->id); - web_client_process(w); + web_client_process_request(w); + + // if the sockets are closed, may have transferred this client + // to plugins.d + if(unlikely(w->mode == WEB_CLIENT_MODE_STREAM)) + break; } } diff --git a/src/web_client.h b/src/web_client.h index 2555a0c24..70c5b1ff0 100644 --- a/src/web_client.h +++ b/src/web_client.h @@ -5,12 +5,20 @@ extern int web_client_timeout; #ifdef NETDATA_WITH_ZLIB -extern int web_enable_gzip, web_gzip_level, web_gzip_strategy, web_donotrack_comply; +extern int web_enable_gzip, + web_gzip_level, + web_gzip_strategy; #endif /* NETDATA_WITH_ZLIB */ -#define WEB_CLIENT_MODE_NORMAL 0 -#define WEB_CLIENT_MODE_FILECOPY 1 -#define WEB_CLIENT_MODE_OPTIONS 2 +extern int respect_web_browser_do_not_track_policy; +extern char *web_x_frame_options; + +typedef enum web_client_mode { + WEB_CLIENT_MODE_NORMAL = 0, + WEB_CLIENT_MODE_FILECOPY = 1, + WEB_CLIENT_MODE_OPTIONS = 2, + WEB_CLIENT_MODE_STREAM = 3 +} WEB_CLIENT_MODE; #define URL_MAX 8192 #define ZLIB_CHUNK 16384 @@ -50,14 +58,14 @@ struct web_client { uint8_t keepalive:1; // if set to 1, the web client will be re-used - uint8_t mode:3; // the operational mode of the client - 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 uint8_t donottrack:1; // 1 = we should not set cookies on this client uint8_t tracking_required:1; // 1 = if the request requires cookies + WEB_CLIENT_MODE mode; // the operational mode of the client + int tcp_cork; // 1 = we have a cork on the socket int ifd; @@ -98,7 +106,7 @@ 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_process_request(struct web_client *w); extern void web_client_reset(struct web_client *w); extern void *web_client_main(void *ptr); @@ -107,4 +115,7 @@ 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); + +extern int mysendfile(struct web_client *w, char *filename); + #endif diff --git a/src/web_server.c b/src/web_server.c index 8e942a59d..593a82a57 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -5,7 +5,8 @@ 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; + +WEB_SERVER_MODE web_server_mode = WEB_SERVER_MODE_MULTI_THREADED; static int shown_server_socket_error = 0; @@ -83,6 +84,29 @@ int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags) { } #endif +WEB_SERVER_MODE web_server_mode_id(const char *mode) { + if(!strcmp(mode, "none")) + return WEB_SERVER_MODE_NONE; + else if(!strcmp(mode, "single") || !strcmp(mode, "single-threaded")) + return WEB_SERVER_MODE_SINGLE_THREADED; + else // if(!strcmp(mode, "multi") || !strcmp(mode, "multi-threaded")) + return WEB_SERVER_MODE_MULTI_THREADED; +} + +const char *web_server_mode_name(WEB_SERVER_MODE id) { + switch(id) { + case WEB_SERVER_MODE_NONE: + return "none"; + + case WEB_SERVER_MODE_SINGLE_THREADED: + return "single-threaded"; + + default: + case WEB_SERVER_MODE_MULTI_THREADED: + return "multi-threaded"; + } +} + int create_listen_socket4(const char *ip, int port, int listen_backlog) { int sock; int sockopt = 1; @@ -316,22 +340,16 @@ static inline int bind_to_one(const char *definition, int default_port, int list int create_listen_sockets(void) { shown_server_socket_error = 0; - 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_backlog = (int) config_get_number(CONFIG_SECTION_WEB, "listen backlog", LISTEN_BACKLOG); - listen_port = (int) config_get_number("global", "default port", LISTEN_PORT); + listen_port = (int) config_get_number(CONFIG_SECTION_WEB, "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); + listen_port = (int) config_set_number(CONFIG_SECTION_WEB, "default port", LISTEN_PORT); } debug(D_OPTIONS, "Default listen port set to %d.", listen_port); - char *s = config_get("global", "bind to", "*"); + char *s = config_get(CONFIG_SECTION_WEB, "bind to", "*"); while(*s) { char *e = s; @@ -614,7 +632,7 @@ void *socket_listen_main_single_threaded(void *ptr) { if (w->mode != WEB_CLIENT_MODE_FILECOPY) { debug(D_WEB_CLIENT, "%llu: Processing received data.", w->id); - web_client_process(w); + web_client_process_request(w); } } diff --git a/src/web_server.h b/src/web_server.h index 93adc5b28..41dcfcf09 100644 --- a/src/web_server.h +++ b/src/web_server.h @@ -13,9 +13,17 @@ #define MAX_LISTEN_FDS 100 #endif -#define WEB_SERVER_MODE_MULTI_THREADED 0 -#define WEB_SERVER_MODE_SINGLE_THREADED 1 -extern int web_server_mode; +typedef enum web_server_mode { + WEB_SERVER_MODE_SINGLE_THREADED, + WEB_SERVER_MODE_MULTI_THREADED, + WEB_SERVER_MODE_NONE +} WEB_SERVER_MODE; + +extern WEB_SERVER_MODE web_server_mode; + +extern WEB_SERVER_MODE web_server_mode_id(const char *mode); +extern const char *web_server_mode_name(WEB_SERVER_MODE id); + extern void *socket_listen_main_multi_threaded(void *ptr); extern void *socket_listen_main_single_threaded(void *ptr); |