From 51f689a8e17ff3929acd2dbf39e936d2cd3ac723 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Sun, 30 Apr 2017 17:09:37 +0100 Subject: New upstream version 1.6.0+dfsg --- .codeclimate.yml | 4 + .gitignore | 2 + ChangeLog | 168 +- Makefile.am | 17 +- Makefile.in | 372 +- README.md | 71 +- aclocal.m4 | 777 ++-- autogen.sh | 2 +- charts.d/Makefile.am | 8 +- charts.d/Makefile.in | 146 +- charts.d/ap.chart.sh | 142 +- charts.d/apache.chart.sh | 0 charts.d/apcupsd.chart.sh | 0 charts.d/cpu_apps.chart.sh | 0 charts.d/cpufreq.chart.sh | 0 charts.d/example.chart.sh | 0 charts.d/hddtemp.chart.sh | 0 charts.d/load_average.chart.sh | 0 charts.d/mem_apps.chart.sh | 0 charts.d/mysql.chart.sh | 0 charts.d/nginx.chart.sh | 0 charts.d/nut.chart.sh | 31 +- charts.d/opensips.chart.sh | 0 charts.d/phpfpm.chart.sh | 0 charts.d/postfix.chart.sh | 0 charts.d/sensors.chart.sh | 0 charts.d/squid.chart.sh | 0 charts.d/tomcat.chart.sh | 0 compile | 347 -- conf.d/Makefile.am | 38 +- conf.d/Makefile.in | 246 +- conf.d/apps_groups.conf | 5 +- conf.d/charts.d/nut.conf | 6 +- conf.d/health.d/cpu.conf | 4 +- conf.d/health.d/disks.conf | 32 +- conf.d/health.d/fping.conf | 53 + conf.d/health.d/ipmi.conf | 20 + conf.d/health.d/memcached.conf | 2 +- conf.d/health.d/mysql.conf | 4 +- conf.d/health.d/net.conf | 18 +- conf.d/health.d/tcp_resets.conf | 6 +- conf.d/health.d/web_log.conf | 161 + conf.d/health_alarm_notify.conf | 37 +- conf.d/node.d/snmp.conf.md | 700 +-- conf.d/python.d.conf | 37 +- conf.d/python.d/elasticsearch.conf | 7 + conf.d/python.d/fail2ban.conf | 19 +- conf.d/python.d/gunicorn_log.conf | 73 - conf.d/python.d/mongodb.conf | 77 + conf.d/python.d/nginx_log.conf | 72 - conf.d/python.d/nsd.conf | 86 + conf.d/python.d/smartd_log.conf | 85 + conf.d/python.d/varnish.conf | 8 - conf.d/python.d/web_log.conf | 147 + conf.d/stream.conf | 143 + config.guess | 489 ++- config.h.in | 67 +- config.sub | 180 +- configs.signatures | 48 +- configure | 6516 ++++++++++++++-------------- configure.ac | 494 ++- contrib/Makefile.in | 126 +- contrib/debian/changelog | 3 - contrib/debian/compat | 1 - contrib/debian/control | 25 - contrib/debian/control.wheezy | 25 - contrib/debian/copyright | 10 - contrib/debian/netdata.conf | 16 - contrib/debian/netdata.default | 5 - contrib/debian/netdata.docs | 1 - contrib/debian/netdata.init | 56 - contrib/debian/netdata.install | 1 - contrib/debian/netdata.lintian-overrides | 16 - contrib/debian/netdata.postinst.in | 41 - contrib/debian/netdata.postrm | 43 - contrib/debian/netdata.service | 14 - contrib/debian/rules | 87 - contrib/debian/source/format | 1 - coverity-scan.sh | 7 +- depcomp | 487 +-- diagrams/netdata-for-ephemeral-nodes.xml | 1 + diagrams/netdata-proxies-example.xml | 1 + install-sh | 374 +- installer/functions.sh | 344 ++ m4/ax_c_lto.m4 | 21 + missing | 414 +- netdata-installer.sh | 933 ++-- netdata.spec | 196 +- netdata.spec.in | 194 +- node.d/Makefile.in | 147 +- node.d/snmp.node.js | 31 +- plugins.d/Makefile.in | 141 +- plugins.d/alarm-notify.sh | 162 +- plugins.d/cgroup-name.sh | 7 + plugins.d/fping.plugin | 31 +- plugins.d/python.d.plugin | 76 +- plugins.d/tc-qos-helper.sh | 100 +- python.d/Makefile.am | 16 +- python.d/Makefile.in | 182 +- python.d/README.md | 378 +- python.d/bind_rndc.chart.py | 48 +- python.d/cpufreq.chart.py | 1 + python.d/elasticsearch.chart.py | 401 +- python.d/fail2ban.chart.py | 97 +- python.d/freeradius.chart.py | 18 +- python.d/gunicorn_log.chart.py | 72 - python.d/isc_dhcpd.chart.py | 37 +- python.d/mongodb.chart.py | 672 +++ python.d/mysql.chart.py | 546 ++- python.d/nginx_log.chart.py | 82 - python.d/nsd.chart.py | 93 + python.d/phpfpm.chart.py | 183 +- python.d/postgres.chart.py | 526 +-- python.d/python_modules/__init__.py | 0 python.d/python_modules/base.py | 372 +- python.d/smartd_log.chart.py | 221 + python.d/tomcat.chart.py | 91 +- python.d/varnish.chart.py | 18 +- python.d/web_log.chart.py | 653 +++ src/Makefile.am | 201 +- src/Makefile.in | 526 +-- src/adaptive_resortable_list.c | 40 +- src/appconfig.c | 310 +- src/appconfig.h | 67 +- src/apps_plugin.c | 2107 +++++---- src/avl.c | 16 +- src/avl.h | 8 +- src/backends.c | 556 ++- src/clocks.c | 114 +- src/clocks.h | 94 +- src/common.c | 81 +- src/common.h | 41 +- src/daemon.c | 154 +- src/dictionary.c | 14 +- src/dictionary.h | 2 +- src/freebsd_sysctl.c | 4885 ++++++++++++++------- src/freeipmi_plugin.c | 1621 +++++++ src/global_statistics.c | 83 +- src/health.c | 3135 ++----------- src/health.h | 88 +- src/health_config.c | 877 ++++ src/health_json.c | 256 ++ src/health_log.c | 465 ++ src/inlined.h | 47 +- src/ipc.c | 22 +- src/locks.h | 294 ++ src/log.c | 6 +- src/log.h | 56 +- src/macos_fw.c | 179 +- src/macos_mach_smi.c | 72 +- src/macos_sysctl.c | 566 +-- src/main.c | 474 +- src/main.h | 2 - src/plugin_checks.c | 31 +- src/plugin_freebsd.c | 156 +- src/plugin_freebsd.h | 113 +- src/plugin_idlejitter.c | 15 +- src/plugin_macos.c | 30 +- src/plugin_macos.h | 4 +- src/plugin_nfacct.c | 855 +++- src/plugin_proc.c | 34 +- src/plugin_proc_diskspace.c | 233 +- src/plugin_tc.c | 679 +-- src/plugins_d.c | 506 +-- src/plugins_d.h | 3 +- src/proc_diskstats.c | 556 ++- src/proc_interrupts.c | 16 +- src/proc_loadavg.c | 31 +- src/proc_meminfo.c | 103 +- src/proc_net_dev.c | 434 +- src/proc_net_ip_vs_stats.c | 31 +- src/proc_net_netstat.c | 254 +- src/proc_net_rpc_nfs.c | 47 +- src/proc_net_rpc_nfsd.c | 171 +- src/proc_net_snmp.c | 203 +- src/proc_net_snmp6.c | 465 +- src/proc_net_softnet_stat.c | 16 +- src/proc_net_stat_conntrack.c | 94 +- src/proc_net_stat_synproxy.c | 62 +- src/proc_self_mountinfo.c | 8 +- src/proc_softirqs.c | 16 +- src/proc_stat.c | 63 +- src/proc_sys_kernel_random_entropy_avail.c | 9 +- src/proc_uptime.c | 9 +- src/proc_vmstat.c | 64 +- src/procfile.c | 298 +- src/procfile.h | 16 +- src/registry.c | 98 +- src/registry.h | 13 +- src/registry_init.c | 41 +- src/registry_internals.c | 36 +- src/registry_internals.h | 8 +- src/registry_machine.c | 2 +- src/registry_person.c | 2 +- src/rrd.c | 1642 +------ src/rrd.h | 460 +- src/rrd2json.c | 755 +--- src/rrd2json.h | 29 +- src/rrd2json_api_old.c | 487 +++ src/rrd2json_api_old.h | 14 + src/rrdcalc.c | 415 ++ src/rrdcalctemplate.c | 62 + src/rrddim.c | 313 ++ src/rrddimvar.c | 210 + src/rrdfamily.c | 58 + src/rrdhost.c | 583 +++ src/rrdpush.c | 761 ++++ src/rrdpush.h | 15 + src/rrdset.c | 1310 ++++++ src/rrdsetvar.c | 120 + src/rrdvar.c | 265 ++ src/simple_pattern.c | 8 +- src/socket.c | 101 +- src/socket.h | 4 + src/sys_devices_system_edac_mc.c | 34 +- src/sys_devices_system_node.c | 90 +- src/sys_fs_cgroup.c | 1081 +++-- src/sys_kernel_mm_ksm.c | 46 +- src/unit_test.c | 67 +- src/web_api_old.c | 237 + src/web_api_old.h | 13 + src/web_api_v1.c | 903 ++++ src/web_api_v1.h | 21 + src/web_buffer.c | 5 +- src/web_buffer_svg.c | 332 +- src/web_buffer_svg.h | 3 +- src/web_client.c | 1791 ++------ src/web_client.h | 25 +- src/web_server.c | 42 +- src/web_server.h | 14 +- system/Makefile.in | 128 +- system/netdata.service.in | 2 +- web/Makefile.am | 3 +- web/Makefile.in | 174 +- web/dashboard.html | 2 +- web/dashboard.js | 1014 +++-- web/dashboard_info.js | 315 +- web/dashboard_info_custom_example.js | 58 + web/index.html | 816 ++-- web/lib/gauge-1.3.2.min.js | 1 + web/lib/gauge-d5260c3.min.js | 1 - web/netdata-swagger.json | 71 +- web/netdata-swagger.yaml | 32 +- web/registry.html | 2 +- web/tv.html | 2 +- web/version.txt | 2 +- 246 files changed, 35853 insertions(+), 23182 deletions(-) mode change 100755 => 100644 charts.d/ap.chart.sh mode change 100755 => 100644 charts.d/apache.chart.sh mode change 100755 => 100644 charts.d/apcupsd.chart.sh mode change 100755 => 100644 charts.d/cpu_apps.chart.sh mode change 100755 => 100644 charts.d/cpufreq.chart.sh mode change 100755 => 100644 charts.d/example.chart.sh mode change 100755 => 100644 charts.d/hddtemp.chart.sh mode change 100755 => 100644 charts.d/load_average.chart.sh mode change 100755 => 100644 charts.d/mem_apps.chart.sh mode change 100755 => 100644 charts.d/mysql.chart.sh mode change 100755 => 100644 charts.d/nginx.chart.sh mode change 100755 => 100644 charts.d/nut.chart.sh mode change 100755 => 100644 charts.d/opensips.chart.sh mode change 100755 => 100644 charts.d/phpfpm.chart.sh mode change 100755 => 100644 charts.d/postfix.chart.sh mode change 100755 => 100644 charts.d/sensors.chart.sh mode change 100755 => 100644 charts.d/squid.chart.sh mode change 100755 => 100644 charts.d/tomcat.chart.sh delete mode 100755 compile create mode 100644 conf.d/health.d/fping.conf create mode 100644 conf.d/health.d/ipmi.conf create mode 100644 conf.d/health.d/web_log.conf delete mode 100644 conf.d/python.d/gunicorn_log.conf create mode 100644 conf.d/python.d/mongodb.conf delete mode 100644 conf.d/python.d/nginx_log.conf create mode 100644 conf.d/python.d/nsd.conf create mode 100644 conf.d/python.d/smartd_log.conf create mode 100644 conf.d/python.d/web_log.conf create mode 100644 conf.d/stream.conf delete mode 100644 contrib/debian/changelog delete mode 100644 contrib/debian/compat delete mode 100644 contrib/debian/control delete mode 100644 contrib/debian/control.wheezy delete mode 100644 contrib/debian/copyright delete mode 100644 contrib/debian/netdata.conf delete mode 100644 contrib/debian/netdata.default delete mode 100644 contrib/debian/netdata.docs delete mode 100755 contrib/debian/netdata.init delete mode 100644 contrib/debian/netdata.install delete mode 100644 contrib/debian/netdata.lintian-overrides delete mode 100644 contrib/debian/netdata.postinst.in delete mode 100644 contrib/debian/netdata.postrm delete mode 100644 contrib/debian/netdata.service delete mode 100755 contrib/debian/rules delete mode 100644 contrib/debian/source/format create mode 100644 diagrams/netdata-for-ephemeral-nodes.xml create mode 100644 diagrams/netdata-proxies-example.xml create mode 100644 installer/functions.sh create mode 100644 m4/ax_c_lto.m4 delete mode 100644 python.d/gunicorn_log.chart.py create mode 100644 python.d/mongodb.chart.py delete mode 100644 python.d/nginx_log.chart.py create mode 100644 python.d/nsd.chart.py mode change 100755 => 100644 python.d/phpfpm.chart.py mode change 100755 => 100644 python.d/python_modules/__init__.py create mode 100644 python.d/smartd_log.chart.py create mode 100644 python.d/web_log.chart.py create mode 100644 src/freeipmi_plugin.c mode change 100755 => 100644 src/health.c create mode 100644 src/health_config.c create mode 100644 src/health_json.c create mode 100644 src/health_log.c create mode 100644 src/locks.h create mode 100644 src/rrd2json_api_old.c create mode 100644 src/rrd2json_api_old.h create mode 100644 src/rrdcalc.c create mode 100644 src/rrdcalctemplate.c create mode 100644 src/rrddim.c create mode 100644 src/rrddimvar.c create mode 100644 src/rrdfamily.c create mode 100644 src/rrdhost.c create mode 100644 src/rrdpush.c create mode 100644 src/rrdpush.h create mode 100644 src/rrdset.c create mode 100644 src/rrdsetvar.c create mode 100644 src/rrdvar.c create mode 100644 src/web_api_old.c create mode 100644 src/web_api_old.h create mode 100644 src/web_api_v1.c create mode 100644 src/web_api_v1.h create mode 100644 web/dashboard_info_custom_example.js create mode 100644 web/lib/gauge-1.3.2.min.js delete mode 100644 web/lib/gauge-d5260c3.min.js diff --git a/.codeclimate.yml b/.codeclimate.yml index 91e0babae..02b452647 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -13,6 +13,8 @@ engines: checks: Similar code: enabled: false + Identical code: + enabled: false eslint: enabled: true checks: @@ -28,6 +30,8 @@ engines: enabled: false no-alert: enabled: false + no-undef-init: + enabled: false fixme: enabled: false phpmd: diff --git a/.gitignore b/.gitignore index e5f27403d..0169e9316 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ stamp-h1 netdata apps.plugin +freeipmi.plugin netdata.spec *.tar.* @@ -37,6 +38,7 @@ netdata-coverity-analysis.tgz .cproject .idea/ +.vscode/ .project README TODO.md diff --git a/ChangeLog b/ChangeLog index c0950dec1..1f6c5f274 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,168 @@ -netdata (1.5.0) - 2016-01-22 +netdata (1.6.0) - 2017-03-20 + + * birthday release: 1 year netdata + + netdata was first published on March 30th, 2016. + It has been a crazy year since then: + + 225.000 unique netdata users + currently, at 1.000 new unique users per day + + 80.000 unique netdata installations + currently, at 500 new installation per day + + 610.000 docker pulls on docker hub + + 4.000.000 netdata sessions served + currently, at 15.000 sessions served per day + + 20.000 github stars + + Thank you! + You are awesome! + + * central netdata is here + + This is the first release that supports real-time streaming of + metrics between netdata servers. + + netdata can now be: + + - autonomous host monitoring + (like it always has been) + + - headless data collector + (collect and stream metrics in real-time to another netdata) + + - headless proxy + (collect metrics from multiple netdata and stream them to another netdata) + + - store and forward proxy + (like headless proxy, but with a local database) + + - central database + (metrics from multiple hosts are aggregated) + + metrics databases can be configured on all nodes and each node maintaining + a database may have a different retention policy and possibly run + (even different) alarms on them. + + * monitoring ephemeral nodes + + netdata now supports monitoring autoscaled ephemeral nodes, + that are started and stopped on demand (their IP is not known). + + When the ephemeral nodes start streaming metrics to the central + netdata, the central netdata will show register them at "my-netdata" + menu on the dashboard. + + For more information check: + https://github.com/firehol/netdata/wiki/monitoring-ephemeral-nodes + + * monitoring ephemeral containers and VM guests + + netdata now cleans up container, guest VM, network interfaces and mounted + disk metrics, disabling automatically their alarms too. + + For more information check: + https://github.com/firehol/netdata/wiki/monitoring-ephemeral-containers + + * apps.plugin ported for FreeBSD + + @vlvkobal has ported "apps.plugin" to FreeBSD. netdata can now provide + "Applications", "Users" and "User Groups" on FreeBSD. + + * web_log plugin + + @l2isbad has done a wonderful job creating a unified web log parsing plugin + for all kinds of web server logs. With it, netdata provides real-time + performance information and health monitoring alarms for web applications + and web sites! + + For more information check: + https://github.com/firehol/netdata/wiki/The-spectacles-of-a-web-server-log-file + + * backends + + netdata can now archive metrics to `JSON` backends + (both push, by @lfdominguez, and pull modes). + + * IPMI monitoring + + netdata now has an IPMI plugin (based on freeipmi) + for monitoring server hardware. + + The plugin creates (up to) 8 charts: + + 1. number of sensors by state + 2. number of events in SEL + 3. Temperatures CELCIUS + 4. Temperatures FAHRENHEIT + 5. Voltages + 6. Currents + 7. Power + 8. Fans + + It also supports alarms (including the number of sensors in critical state). + + For more information, check: + https://github.com/firehol/netdata/wiki/monitoring-IPMI + + * new plugins + + @l2isbad builds python data collection plugins for netdata at an wonderfull + rate! He rocks! + + - **web_log** for monitoring in real-time all kinds of web server log files @l2isbad + - **freeipmi** for monitoring IPMI (server hardware) + - **nsd** (the [name server daemon](https://www.nlnetlabs.nl/projects/nsd/)) @383c57 + - **mongodb** @l2isbad + - **smartd_log** (monitoring disk S.M.A.R.T. values) @l2isbad + + * improved plugins + + - **nfacct** reworked and now collects connection tracker information using netlink. + - **ElasticSearch** re-worked @l2isbad + - **mysql** re-worked to allow faster development of custom mysql based plugins (MySQLService) @l2isbad + - **SNMP** + - **tomcat** @NMcCloud + - **ap** (monitoring hostapd access points) + - **php_fpm** @l2isbad + - **postgres** @l2isbad + - **isc_dhcpd** @l2isbad + - **bind_rndc** @l2isbad + - **numa** + - **apps.plugin** improvements and freebsd support @vlvkobal + - **fail2ban** @l2isbad + - **freeradius** @l2isbad + - **nut** (monitoring UPSes) + - **tc** (Linux QoS) now works on qdiscs instead of classes for the same result (a lot faster) @t-h-e + - **varnish** @l2isbad + + * new and improved alarms + - **web_log**, many alarms to detect common web site/API issues + - **fping**, alarms to detect packet loss, disconnects and unusually high latency + - **cpu**, cpu utilization alarm now ignores `nice` + + * new and improved alarm notification methods + - **HipChat** to allow hosted HipChat @frei-style + - **discordapp** @lowfive + + * dashboard improvements + - dashboard now works on HiDPi screens + - dashboard now shows version of netdata + - dashboard now resets charts properly + - dashboard updated to use latest gauge.js release + + * other improvements + - thanks to @rlefevre netdata now uses a lot of different high resolution system clocks. + + netdata has received a lot more improvements from many more contributors! + + Thank you all! + + +netdata (1.5.0) - 2017-01-22 * yet another release that makes netdata the fastest netdata ever! @@ -27,7 +191,7 @@ netdata (1.5.0) - 2016-01-22 Ilya Mashchenko (@l2isbad) has created most of the python data collection plugins in this release ! - - Systemd Services (using cgroups!) + - systemd Services (using cgroups!) - FPing (yes, network latency in netdata!) - postgres databases @facetoe, @moumoul - Vanish disk cache (v3 and v4) @l2isbad diff --git a/Makefile.am b/Makefile.am index ab77bc734..3ccf82f8f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,8 +23,18 @@ EXTRA_DIST = \ .eslintignore \ .eslintrc \ .travis \ - m4/ax_check_enable_debug.m4 \ - m4/ax_c_statement_expressions.m4 \ + m4/jemalloc.m4 \ + m4/ax_c___atomic.m4 \ + m4/ax_check_enable_debug.m4 \ + m4/ax_c_mallinfo.m4 \ + m4/ax_gcc_func_attribute.m4 \ + m4/ax_check_compile_flag.m4 \ + m4/ax_c_statement_expressions.m4 \ + m4/ax_pthread.m4 \ + m4/ax_c_lto.m4 \ + m4/ax_c_mallopt.m4 \ + m4/tcmalloc.m4 \ + m4/ax_c__generic.m4 \ autogen.sh \ README.md \ LICENSE.md \ @@ -48,6 +58,8 @@ SUBDIRS = \ dist_noinst_DATA= \ diagrams/config.puml \ diagrams/registry.puml \ + diagrams/netdata-for-ephemeral-nodes.xml \ + diagrams/netdata-proxies-example.xml \ configs.signatures \ Dockerfile \ netdata.spec \ @@ -60,4 +72,5 @@ dist_noinst_SCRIPTS= \ coverity-scan.sh \ docker-build.sh \ netdata-installer.sh \ + installer/functions.sh \ $(NULL) diff --git a/Makefile.in b/Makefile.in index 412345f61..492376f5e 100644 --- a/Makefile.in +++ b/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,9 +36,14 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = . +DIST_COMMON = $(am__configure_deps) $(dist_noinst_DATA) \ + $(dist_noinst_SCRIPTS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/config.h.in \ + $(srcdir)/netdata.spec.in $(top_srcdir)/configure COPYING \ + ChangeLog config.guess config.sub depcomp install-sh missing 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 \ @@ -101,9 +52,6 @@ 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 $(top_srcdir)/configure \ - $(am__configure_deps) $(dist_noinst_SCRIPTS) \ - $(dist_noinst_DATA) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d @@ -111,67 +59,24 @@ CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = netdata.spec CONFIG_CLEAN_VPATH_FILES = SCRIPTS = $(dist_noinst_SCRIPTS) -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 = SOURCES = DIST_SOURCES = -RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ - ctags-recursive dvi-recursive html-recursive info-recursive \ - install-data-recursive install-dvi-recursive \ - install-exec-recursive install-html-recursive \ - install-info-recursive install-pdf-recursive \ - install-ps-recursive install-recursive installcheck-recursive \ - installdirs-recursive pdf-recursive ps-recursive \ - tags-recursive uninstall-recursive -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive DATA = $(dist_noinst_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive -am__recursive_targets = \ - $(RECURSIVE_TARGETS) \ - $(RECURSIVE_CLEAN_TARGETS) \ - $(am__extra_recursive_targets) -AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - cscope distdir dist dist-all distcheck -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ - $(LISP)config.h.in -# 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)` +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir dist dist-all distcheck ETAGS = etags CTAGS = ctags -CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) -am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ - $(srcdir)/netdata.spec.in COPYING ChangeLog compile \ - config.guess config.sub install-sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) @@ -181,7 +86,6 @@ am__remove_distdir = \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi -am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ @@ -209,14 +113,12 @@ am__relativize = \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz $(distdir).tar.bz2 $(distdir).tar.xz GZIP_ENV = --best -DIST_TARGETS = dist-xz dist-bzip2 dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -240,7 +142,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@ @@ -254,6 +160,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@ @@ -375,8 +285,18 @@ EXTRA_DIST = \ .eslintignore \ .eslintrc \ .travis \ - m4/ax_check_enable_debug.m4 \ - m4/ax_c_statement_expressions.m4 \ + m4/jemalloc.m4 \ + m4/ax_c___atomic.m4 \ + m4/ax_check_enable_debug.m4 \ + m4/ax_c_mallinfo.m4 \ + m4/ax_gcc_func_attribute.m4 \ + m4/ax_check_compile_flag.m4 \ + m4/ax_c_statement_expressions.m4 \ + m4/ax_pthread.m4 \ + m4/ax_c_lto.m4 \ + m4/ax_c_mallopt.m4 \ + m4/tcmalloc.m4 \ + m4/ax_c__generic.m4 \ autogen.sh \ README.md \ LICENSE.md \ @@ -400,6 +320,8 @@ SUBDIRS = \ dist_noinst_DATA = \ diagrams/config.puml \ diagrams/registry.puml \ + diagrams/netdata-for-ephemeral-nodes.xml \ + diagrams/netdata-proxies-example.xml \ configs.signatures \ Dockerfile \ netdata.spec \ @@ -413,6 +335,7 @@ dist_noinst_SCRIPTS = \ coverity-scan.sh \ docker-build.sh \ netdata-installer.sh \ + installer/functions.sh \ $(NULL) all: config.h @@ -434,6 +357,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -454,8 +378,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): config.h: stamp-h1 - @test -f $@ || rm -f stamp-h1 - @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + @if test ! -f $@; then rm -f stamp-h1; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) stamp-h1; else :; fi stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 @@ -471,25 +395,22 @@ netdata.spec: $(top_builddir)/config.status $(srcdir)/netdata.spec.in cd $(top_builddir) && $(SHELL) ./config.status $@ # This directory's subdirectories are mostly independent; you can cd -# into them and run 'make' without going through this Makefile. -# To change the values of 'make' variables: instead of editing Makefiles, -# (1) if the variable is set in 'config.status', edit 'config.status' -# (which will cause the Makefiles to be regenerated when you run 'make'); -# (2) otherwise, pass the desired values on the 'make' command line. -$(am__recursive_targets): - @fail=; \ - if $(am__make_keepgoing); then \ - failcom='fail=yes'; \ - else \ - failcom='exit 1'; \ - fi; \ +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ - case "$@" in \ - distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ - *) list='$(SUBDIRS)' ;; \ - esac; \ - for subdir in $$list; do \ + list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ @@ -504,12 +425,57 @@ $(am__recursive_targets): $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-recursive -TAGS: tags +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done -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: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ @@ -525,7 +491,12 @@ tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ - $(am__define_uniq_tagged_files); \ + list='$(SOURCES) $(HEADERS) config.h.in $(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; \ @@ -537,11 +508,15 @@ tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $$unique; \ fi; \ fi -ctags: ctags-recursive - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) config.h.in $(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 @@ -550,31 +525,9 @@ GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" -cscope: cscope.files - test ! -s cscope.files \ - || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) -clean-cscope: - -rm -f cscope.files -cscope.files: clean-cscope cscopelist -cscopelist: cscopelist-recursive - -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 - -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) @@ -610,10 +563,13 @@ distdir: $(DISTFILES) done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ - $(am__make_dryrun) \ - || test -d "$(distdir)/$$subdir" \ - || $(MKDIR_P) "$(distdir)/$$subdir" \ - || exit 1; \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ @@ -642,40 +598,40 @@ distdir: $(DISTFILES) || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz - $(am__post_remove_distdir) + $(am__remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 - $(am__post_remove_distdir) + $(am__remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz - $(am__post_remove_distdir) + $(am__remove_distdir) + +dist-lzma: distdir + tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma + $(am__remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz - $(am__post_remove_distdir) + $(am__remove_distdir) dist-tarZ: distdir - @echo WARNING: "Support for distribution archives compressed with" \ - "legacy program 'compress' is deprecated." >&2 - @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z - $(am__post_remove_distdir) + $(am__remove_distdir) dist-shar: distdir - @echo WARNING: "Support for shar distribution archives is" \ - "deprecated." >&2 - @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz - $(am__post_remove_distdir) + $(am__remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) - $(am__post_remove_distdir) + $(am__remove_distdir) -dist dist-all: - $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' - $(am__post_remove_distdir) +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another @@ -686,6 +642,8 @@ distcheck: dist GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lzma*) \ + lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ @@ -697,19 +655,18 @@ distcheck: dist *.zip*) \ unzip $(distdir).zip ;;\ esac - chmod -R a-w $(distdir) - chmod u+w $(distdir) - mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ - && $(am__cd) $(distdir)/_build/sub \ - && ../../configure \ + && $(am__cd) $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ - --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ @@ -732,7 +689,7 @@ distcheck: dist && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 - $(am__post_remove_distdir) + $(am__remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' @@ -867,12 +824,13 @@ ps-am: uninstall-am: -.MAKE: $(am__recursive_targets) all install-am install-strip +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all \ + ctags-recursive install-am install-strip tags-recursive -.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ - am--refresh check check-am clean clean-cscope clean-generic \ - cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ - dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am am--refresh check check-am clean clean-generic \ + ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \ + dist-lzip dist-lzma dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-generic distclean-hdr \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ @@ -882,10 +840,8 @@ uninstall-am: install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs installdirs-am \ maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am - -.PRECIOUS: Makefile + mostlyclean-generic pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/README.md b/README.md index 5165078d4..cff7f31bd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# netdata [![Build Status](https://travis-ci.org/firehol/netdata.svg?branch=master)](https://travis-ci.org/firehol/netdata) [![Coverity Scan Build Status](https://scan.coverity.com/projects/9140/badge.svg)](https://scan.coverity.com/projects/firehol-netdata) [![Code Climate](https://codeclimate.com/github/firehol/netdata/badges/gpa.svg)](https://codeclimate.com/github/firehol/netdata) [![Docker Pulls](https://img.shields.io/docker/pulls/titpetric/netdata.svg)](https://hub.docker.com/r/titpetric/netdata/) +# netdata [![Build Status](https://travis-ci.org/firehol/netdata.svg?branch=master)](https://travis-ci.org/firehol/netdata) [![Coverity Scan Build Status](https://scan.coverity.com/projects/9140/badge.svg)](https://scan.coverity.com/projects/firehol-netdata) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a994873f30d045b9b4b83606c3eb3498)](https://www.codacy.com/app/netdata/netdata?utm_source=github.com&utm_medium=referral&utm_content=firehol/netdata&utm_campaign=Badge_Grade) [![Code Climate](https://codeclimate.com/github/firehol/netdata/badges/gpa.svg)](https://codeclimate.com/github/firehol/netdata) > *New to netdata? Here is a live demo: [http://my-netdata.io](http://my-netdata.io)* **netdata** is a system for **distributed real-time performance and health monitoring**. @@ -10,12 +10,16 @@ _netdata is **fast** and **efficient**, designed to permanently run on all syste (**physical** & **virtual** servers, **containers**, **IoT** devices), without disrupting their core function._ +netdata runs on **Linux**, **FreeBSD**, and **MacOS**. + +[![Twitter Follow](https://img.shields.io/twitter/follow/linuxnetdata.svg?style=social&label=New%20-%20stay%20in%20touch%20-%20follow%20netdata%20on%20twitter)](https://twitter.com/linuxnetdata) + --- ## User base *Since May 16th 2016 (the date the [global public netdata registry](https://github.com/firehol/netdata/wiki/mynetdata-menu-item) was released):*
-[![User Base](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=persons&label=user%20base&units=null&value_color=blue&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Monitored Servers](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=machines&label=servers%20monitored&units=null&value_color=orange&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Sessions Served](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_sessions&label=sessions%20served&units=null&value_color=yellowgreen&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) +[![User Base](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=persons&label=user%20base&units=null&value_color=blue&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Monitored Servers](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=machines&label=servers%20monitored&units=null&value_color=orange&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Sessions Served](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_sessions&label=sessions%20served&units=null&value_color=yellowgreen&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Docker Pulls](https://img.shields.io/docker/pulls/titpetric/netdata.svg)](https://hub.docker.com/r/titpetric/netdata/) *in the last 24 hours:*
[![New Users Today](http://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=persons&after=-86400&options=unaligned&group=incremental-sum&label=new%20users%20today&units=null&value_color=blue&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![New Machines Today](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_entries&dimensions=machines&group=incremental-sum&after=-86400&options=unaligned&label=servers%20added%20today&units=null&value_color=orange&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) [![Sessions Today](https://registry.my-netdata.io/api/v1/badge.svg?chart=netdata.registry_sessions&after=-86400&group=incremental-sum&options=unaligned&label=sessions%20served%20today&units=null&value_color=yellowgreen&precision=0&v42)](https://registry.my-netdata.io/#menu_netdata_submenu_registry) @@ -29,15 +33,16 @@ Netdata is featured at

-`Jan 22nd, 2017` - **[netdata v1.5.0 released!](https://github.com/firehol/netdata/releases)** +`Mar 20th, 2017` - **[netdata v1.6.0 released!](https://github.com/firehol/netdata/releases)** - - netdata now runs on **FreeBSD** and **MacOS** - - netdata now supports **Graphite**, **OpenTSDB**, **Prometheus** and compatible backends - - netdata now monitors **SystemD Services** - - new plugins: fping, postgres, varnish, elasticsearch, haproxy, freeradius, mdstat, ISC dhcpd, fail2ban, openvpn, NUMA memory, CPU Idle States, gunicorn, ECC memory errors, IPC semaphores, uptime - - improved plugins: netfilter conntrack, mysql/mariadb, ipfs, cpufreq, hddtemp, sensors, nginx, nginx_log, phpfpm, redis, dovecot, containers and cgroups, disk space, apps.plugin, tc (QoS) and almost all internal plugins (memory, IPv4 and IPv6, network interfaces, QoS, etc) - - dozens of new and improved alarms (including performance monitoring alarms for mysql) - - new alarm notifications: messagebird.com, pagerduty.com, pushbullet.com, twilio.com, hipchat, kafka + - central netdata is here! headless collectors, proxies, streaming of metrics, etc. + - [monitoring ephemeral nodes (auto-scaled VMs)](https://github.com/firehol/netdata/wiki/monitoring-ephemeral-nodes) + - [monitoring ephemeral containers and VM guests](https://github.com/firehol/netdata/wiki/monitoring-ephemeral-containers) + - [monitoring web servers](https://github.com/firehol/netdata/wiki/The-spectacles-of-a-web-server-log-file) + - apps.plugin ported for FreeBSD + - [monitoring IPMI](https://github.com/firehol/netdata/wiki/monitoring-IPMI) + - dozens of new and improved plugins + - dozens of new and improved alarms - dozens more improvements and performance optimizations --- @@ -60,9 +65,10 @@ Netdata is featured at sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with - # Solaris 10 /bin/sh. - echo '/* dummy */' > sub/conftst$i.h + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - # We check with '-c' and '-o' for the sake of the "dashmstdout" + # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly - # handle '-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs. + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in @@ -459,8 +414,8 @@ AC_CACHE_CHECK([dependency style of $depcc], test "$am__universal" = false || continue ;; nosideeffect) - # After this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested. + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else @@ -468,7 +423,7 @@ AC_CACHE_CHECK([dependency style of $depcc], fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok '-c -o', but also, the minuso test has + # This compiler won't grok `-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} @@ -516,7 +471,7 @@ AM_CONDITIONAL([am__fastdep$1], [ # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. -# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl @@ -526,13 +481,9 @@ AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], -[AC_ARG_ENABLE([dependency-tracking], [dnl -AS_HELP_STRING( - [--enable-dependency-tracking], - [do not reject slow dependency extractors]) -AS_HELP_STRING( - [--disable-dependency-tracking], - [speeds up one-time build])]) +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' @@ -547,18 +498,20 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. # # This file 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. +#serial 5 # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ - # Older Autoconf quotes --file arguments for eval, but not when files + # Autoconf 2.62 quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in @@ -571,7 +524,7 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but + # We used to match only the files named `Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. @@ -583,19 +536,21 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], continue fi # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. + # from the Makefile without running `make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue + test -z "am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` @@ -613,7 +568,7 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking -# is enabled. FIXME. This creates each '.P' file that we will +# is enabled. FIXME. This creates each `.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], @@ -623,21 +578,18 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2008, 2009 Free Software Foundation, Inc. # # This file 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. +# serial 16 + # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. -dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. -m4_define([AC_PROG_CC], -m4_defn([AC_PROG_CC]) -[_AM_PROG_CC_C_O -]) - # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- @@ -650,7 +602,7 @@ m4_defn([AC_PROG_CC]) # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], -[AC_PREREQ([2.65])dnl +[AC_PREREQ([2.62])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl @@ -679,42 +631,33 @@ AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], -[AC_DIAGNOSE([obsolete], - [$0: two- and three-arguments forms are deprecated.]) -m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. -m4_if( - m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), - [ok:ok],, +m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, -[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) - AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl -AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) -AM_MISSING_PROG([AUTOCONF], [autoconf]) -AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) -AM_MISSING_PROG([AUTOHEADER], [autoheader]) -AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl -AC_REQUIRE([AC_PROG_MKDIR_P])dnl -# For better backward compatibility. To be removed once Automake 1.9.x -# dies out for good. For more background, see: -# -# -AC_SUBST([mkdir_p], ['$(MKDIR_P)']) -# We need awk for the "check" target (and possibly the TAP driver). The -# system "awk" is bad on some platforms. +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl @@ -723,82 +666,34 @@ _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], - [_AM_DEPENDENCIES([CC])], - [m4_define([AC_PROG_CC], - m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], - [_AM_DEPENDENCIES([CXX])], - [m4_define([AC_PROG_CXX], - m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], - [_AM_DEPENDENCIES([OBJC])], - [m4_define([AC_PROG_OBJC], - m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl -AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], - [_AM_DEPENDENCIES([OBJCXX])], - [m4_define([AC_PROG_OBJCXX], - m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl + [_AM_DEPENDENCIES(OBJC)], + [define([AC_PROG_OBJC], + defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl ]) -AC_REQUIRE([AM_SILENT_RULES])dnl -dnl The testsuite driver may need to know about EXEEXT, so add the -dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This -dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl +dnl The `parallel-tests' driver may need to know about EXEEXT, so add the +dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro +dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl - -# POSIX will say in a future version that running "rm -f" with no argument -# is OK; and we want to be able to make that assumption in our Makefile -# recipes. So use an aggressive probe to check that the usage we want is -# actually supported "in the wild" to an acceptable degree. -# See automake bug#10828. -# To make any issue more visible, cause the running configure to be aborted -# by default if the 'rm' program in use doesn't match our expectations; the -# user can still override this though. -if rm -f && rm -fr && rm -rf; then : OK; else - cat >&2 <<'END' -Oops! - -Your 'rm' program seems unable to run without file operands specified -on the command line, even when the '-f' option is present. This is contrary -to the behaviour of most rm programs out there, and not conforming with -the upcoming POSIX standard: - -Please tell bug-automake@gnu.org about your system, including the value -of your $PATH and any error possibly output before this message. This -can help us improve future automake versions. - -END - if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then - echo 'Configuration will proceed anyway, since you have set the' >&2 - echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 - echo >&2 - else - cat >&2 <<'END' -Aborting the configuration process, to ensure you take notice of the issue. - -You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . - -If you want to complete the configuration process using your problematic -'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM -to "yes", and re-run configure. - -END - AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) - fi -fi -dnl The trailing newline in this macro's definition is deliberate, for -dnl backward compatibility and to allow trailing 'dnl'-style comments -dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) -dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. @@ -820,18 +715,21 @@ for _am_header in $config_headers :; do done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001, 2003, 2005, 2008, 2011 Free Software Foundation, +# Inc. # # This file 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. +# serial 1 + # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl -if test x"${install_sh+set}" != xset; then +if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; @@ -839,14 +737,16 @@ if test x"${install_sh+set}" != xset; then install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi -AC_SUBST([install_sh])]) +AC_SUBST(install_sh)]) -# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# Copyright (C) 2003, 2005 Free Software Foundation, Inc. # # This file 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. +# serial 2 + # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], @@ -863,17 +763,20 @@ AC_SUBST([am__leading_dot])]) # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008, +# 2011 Free Software Foundation, Inc. # # This file 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. +# serial 5 + # AM_MAINTAINER_MODE([DEFAULT-MODE]) # ---------------------------------- # Control maintainer-specific portions of Makefiles. -# Default is to disable them, unless 'enable' is passed literally. -# For symmetry, 'disable' may be passed as well. Anyway, the user +# Default is to disable them, unless `enable' is passed literally. +# For symmetry, `disable' may be passed as well. Anyway, the user # can override the default with the --enable/--disable switch. AC_DEFUN([AM_MAINTAINER_MODE], [m4_case(m4_default([$1], [disable]), @@ -884,11 +787,10 @@ AC_DEFUN([AM_MAINTAINER_MODE], AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) dnl maintainer-mode's default is 'disable' unless 'enable' is passed AC_ARG_ENABLE([maintainer-mode], - [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], - am_maintainer_other[ make rules and dependencies not useful - (and sometimes confusing) to the casual installer])], - [USE_MAINTAINER_MODE=$enableval], - [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) +[ --][am_maintainer_other][-maintainer-mode am_maintainer_other make rules and dependencies not useful + (and sometimes confusing) to the casual installer], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) AC_MSG_RESULT([$USE_MAINTAINER_MODE]) AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) MAINT=$MAINTAINER_MODE_TRUE @@ -896,14 +798,18 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) ] ) +AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) + # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. # # This file 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. +# serial 4 + # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. @@ -921,7 +827,7 @@ am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. +# Ignore all kinds of additional output from `make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include @@ -948,12 +854,15 @@ rm -f confinc confmf # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. # # This file 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. +# serial 6 + # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], @@ -961,10 +870,11 @@ AC_DEFUN([AM_MISSING_PROG], $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) + # AM_MISSING_HAS_RUN # ------------------ -# Define MISSING if not defined so far and test if it is modern enough. -# If it is, set am_missing_run to use it, otherwise, to nothing. +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl @@ -977,22 +887,54 @@ if test x"${MISSING+set}" != xset; then esac fi # Use eval to expand $SHELL -if eval "$MISSING --is-lightweight"; then - am_missing_run="$MISSING " +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " else am_missing_run= - AC_MSG_WARN(['missing' script is too old or missing]) + AC_MSG_WARN([`missing' script is too old or missing]) fi ]) +# Copyright (C) 2003, 2004, 2005, 2006, 2011 Free Software Foundation, +# Inc. +# +# This file 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. + +# serial 1 + +# AM_PROG_MKDIR_P +# --------------- +# Check for `mkdir -p'. +AC_DEFUN([AM_PROG_MKDIR_P], +[AC_PREREQ([2.60])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, +dnl while keeping a definition of mkdir_p for backward compatibility. +dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. +dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of +dnl Makefile.ins that do not define MKDIR_P, so we do our own +dnl adjustment using top_builddir (which is defined more often than +dnl MKDIR_P). +AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl +case $mkdir_p in + [[\\/$]]* | ?:[[\\/]]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac +]) + # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001, 2002, 2003, 2005, 2008, 2010 Free Software +# Foundation, Inc. # # This file 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. +# serial 5 + # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], @@ -1002,7 +944,7 @@ AC_DEFUN([_AM_MANGLE_OPTION], # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], -[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ @@ -1016,82 +958,24 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2014 Free Software Foundation, Inc. -# -# This file 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. - -# _AM_PROG_CC_C_O -# --------------- -# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC -# to automatically call this. -AC_DEFUN([_AM_PROG_CC_C_O], -[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl -AC_REQUIRE_AUX_FILE([compile])dnl -AC_LANG_PUSH([C])dnl -AC_CACHE_CHECK( - [whether $CC understands -c and -o together], - [am_cv_prog_cc_c_o], - [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) - # Make sure it works both with $CC and with simple cc. - # Following AC_PROG_CC_C_O, we do the test twice because some - # compilers refuse to overwrite an existing .o file with -o, - # though they will create one. - am_cv_prog_cc_c_o=yes - for am_i in 1 2; do - if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ - && test -f conftest2.$ac_objext; then - : OK - else - am_cv_prog_cc_c_o=no - break - fi - done - rm -f core conftest* - unset am_i]) -if test "$am_cv_prog_cc_c_o" != yes; then - # Losing compiler, so override with the script. - # FIXME: It is wrong to rewrite CC. - # But if we don't then we get into trouble of one sort or another. - # A longer-term fix would be to have automake use am__CC in this case, - # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" - CC="$am_aux_dir/compile $CC" -fi -AC_LANG_POP([C])]) - -# For backward compatibility. -AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) - -# Copyright (C) 2001-2014 Free Software Foundation, Inc. -# -# This file 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. - -# AM_RUN_LOG(COMMAND) -# ------------------- -# Run COMMAND, save the exit status in ac_status, and log it. -# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) -AC_DEFUN([AM_RUN_LOG], -[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD - ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - (exit $ac_status); }]) - # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 +# Free Software Foundation, Inc. # # This file 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. +# serial 5 + # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' @@ -1102,40 +986,32 @@ case `pwd` in esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) - AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; + AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; esac -# Do 'set' in a subshell so we don't clobber the current shell's +# Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( - am_has_slept=no - for am_try in 1 2; do - echo "timestamp, slept: $am_has_slept" > conftest.file - set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` - if test "$[*]" = "X"; then - # -L didn't work. - set X `ls -t "$srcdir/configure" conftest.file` - fi - if test "$[*]" != "X $srcdir/configure conftest.file" \ - && test "$[*]" != "X conftest.file $srcdir/configure"; then - - # If neither matched, then we have a broken ls. This can happen - # if, for instance, CONFIG_SHELL is bash and it inherits a - # broken ls alias from the environment. This has actually - # happened. Such a system could not be considered "sane". - AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken - alias in your environment]) - fi - if test "$[2]" = conftest.file || test $am_try -eq 2; then - break - fi - # Just in case. - sleep 1 - am_has_slept=yes - done + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + test "$[2]" = conftest.file ) then @@ -1145,118 +1021,46 @@ else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi -AC_MSG_RESULT([yes]) -# If we didn't sleep, we still need to ensure time stamps of config.status and -# generated files are strictly newer. -am_sleep_pid= -if grep 'slept: no' conftest.file >/dev/null 2>&1; then - ( sleep 1 ) & - am_sleep_pid=$! -fi -AC_CONFIG_COMMANDS_PRE( - [AC_MSG_CHECKING([that generated files are newer than configure]) - if test -n "$am_sleep_pid"; then - # Hide warnings about reused PIDs. - wait $am_sleep_pid 2>/dev/null - fi - AC_MSG_RESULT([done])]) -rm -f conftest.file -]) +AC_MSG_RESULT(yes)]) -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc. # # This file 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. -# AM_SILENT_RULES([DEFAULT]) -# -------------------------- -# Enable less verbose build rules; with the default set to DEFAULT -# ("yes" being less verbose, "no" or empty being verbose). -AC_DEFUN([AM_SILENT_RULES], -[AC_ARG_ENABLE([silent-rules], [dnl -AS_HELP_STRING( - [--enable-silent-rules], - [less verbose build output (undo: "make V=1")]) -AS_HELP_STRING( - [--disable-silent-rules], - [verbose build output (undo: "make V=0")])dnl -]) -case $enable_silent_rules in @%:@ ((( - yes) AM_DEFAULT_VERBOSITY=0;; - no) AM_DEFAULT_VERBOSITY=1;; - *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; -esac -dnl -dnl A few 'make' implementations (e.g., NonStop OS and NextStep) -dnl do not support nested variable expansions. -dnl See automake bug#9928 and bug#10237. -am_make=${MAKE-make} -AC_CACHE_CHECK([whether $am_make supports nested variables], - [am_cv_make_support_nested_variables], - [if AS_ECHO([['TRUE=$(BAR$(V)) -BAR0=false -BAR1=true -V=1 -am__doit: - @$(TRUE) -.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then - am_cv_make_support_nested_variables=yes -else - am_cv_make_support_nested_variables=no -fi]) -if test $am_cv_make_support_nested_variables = yes; then - dnl Using '$V' instead of '$(V)' breaks IRIX make. - AM_V='$(V)' - AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' -else - AM_V=$AM_DEFAULT_VERBOSITY - AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY -fi -AC_SUBST([AM_V])dnl -AM_SUBST_NOTMAKE([AM_V])dnl -AC_SUBST([AM_DEFAULT_V])dnl -AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl -AC_SUBST([AM_DEFAULT_VERBOSITY])dnl -AM_BACKSLASH='\' -AC_SUBST([AM_BACKSLASH])dnl -_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl -]) - -# Copyright (C) 2001-2014 Free Software Foundation, Inc. -# -# This file 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. +# serial 1 # AM_PROG_INSTALL_STRIP # --------------------- -# One issue with vendor 'install' (even GNU) is that you can't +# One issue with vendor `install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we -# always use install-sh in "make install-strip", and initialize +# always use install-sh in `make install-strip', and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl -# Installed binaries are usually stripped using 'strip' when the user -# run "make install-strip". However 'strip' might not be the right +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right # tool to use in cross-compilation environments, therefore Automake -# will honor the 'STRIP' environment variable to overrule this program. -dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# Copyright (C) 2006, 2008, 2010 Free Software Foundation, Inc. # # This file 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. +# serial 3 + # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. @@ -1270,16 +1074,18 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2014 Free Software Foundation, Inc. +# Copyright (C) 2004, 2005, 2012 Free Software Foundation, Inc. # # This file 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. +# serial 2 + # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. -# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# FORMAT should be one of `v7', `ustar', or `pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory @@ -1289,120 +1095,83 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar -# AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) - -# We'll loop over all known methods to create a tar archive until one works. +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac -m4_if([$1], [v7], - [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], - - [m4_case([$1], - [ustar], - [# The POSIX 1988 'ustar' format is defined with fixed-size fields. - # There is notably a 21 bits limit for the UID and the GID. In fact, - # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 - # and bug#13588). - am_max_uid=2097151 # 2^21 - 1 - am_max_gid=$am_max_uid - # The $UID and $GID variables are not portable, so we need to resort - # to the POSIX-mandated id(1) utility. Errors in the 'id' calls - # below are definitely unexpected, so allow the users to see them - # (that is, avoid stderr redirection). - am_uid=`id -u || echo unknown` - am_gid=`id -g || echo unknown` - AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) - if test $am_uid -le $am_max_uid; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - _am_tools=none - fi - AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) - if test $am_gid -le $am_max_gid; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - _am_tools=none - fi], - - [pax], - [], - - [m4_fatal([Unknown tar format])]) - - AC_MSG_CHECKING([how to create a $1 tar archive]) - - # Go ahead even if we have the value already cached. We do so because we - # need to set the values for the 'am__tar' and 'am__untar' variables. - _am_tools=${am_cv_prog_tar_$1-$_am_tools} - - for _am_tool in $_am_tools; do - case $_am_tool in - gnutar) - for _am_tar in tar gnutar gtar; do - AM_RUN_LOG([$_am_tar --version]) && break - done - am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' - am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' - am__untar="$_am_tar -xf -" - ;; - plaintar) - # Must skip GNU tar: if it does not support --format= it doesn't create - # ustar tarball either. - (tar --version) >/dev/null 2>&1 && continue - am__tar='tar chf - "$$tardir"' - am__tar_='tar chf - "$tardir"' - am__untar='tar xf -' - ;; - pax) - am__tar='pax -L -x $1 -w "$$tardir"' - am__tar_='pax -L -x $1 -w "$tardir"' - am__untar='pax -r' - ;; - cpio) - am__tar='find "$$tardir" -print | cpio -o -H $1 -L' - am__tar_='find "$tardir" -print | cpio -o -H $1 -L' - am__untar='cpio -i -H $1 -d' - ;; - none) - am__tar=false - am__tar_=false - am__untar=false - ;; - esac + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break - # If the value was cached, stop now. We just wanted to have am__tar - # and am__untar set. - test -n "${am_cv_prog_tar_$1}" && break - - # tar/untar a dummy directory, and stop if the command works. - rm -rf conftest.dir - mkdir conftest.dir - echo GrepMe > conftest.dir/file - AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) - rm -rf conftest.dir - if test -s conftest.tar; then - AM_RUN_LOG([$am__untar /dev/null 2>&1 && break - fi - done + # tar/untar a dummy directory, and stop if the command works rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir - AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) - AC_MSG_RESULT([$am_cv_prog_tar_$1])]) - +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/ax_c___atomic.m4]) m4_include([m4/ax_c__generic.m4]) +m4_include([m4/ax_c_lto.m4]) m4_include([m4/ax_c_mallinfo.m4]) m4_include([m4/ax_c_mallopt.m4]) m4_include([m4/ax_check_compile_flag.m4]) diff --git a/autogen.sh b/autogen.sh index 3b076d1a1..38e2ed159 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,2 +1,2 @@ -#!/bin/sh +#!/usr/bin/env sh autoreconf -ivf diff --git a/charts.d/Makefile.am b/charts.d/Makefile.am index ec0e101f3..85bcef3cf 100644 --- a/charts.d/Makefile.am +++ b/charts.d/Makefile.am @@ -4,6 +4,10 @@ MAINTAINERCLEANFILES= $(srcdir)/Makefile.in dist_charts_SCRIPTS = \ + $(NULL) + +dist_charts_DATA = \ + README.md \ ap.chart.sh \ apcupsd.chart.sh \ apache.chart.sh \ @@ -24,7 +28,3 @@ dist_charts_SCRIPTS = \ squid.chart.sh \ tomcat.chart.sh \ $(NULL) - -dist_charts_DATA = \ - README.md \ - $(NULL) diff --git a/charts.d/Makefile.in b/charts.d/Makefile.in index ef63f78f3..a613e1b31 100644 --- a/charts.d/Makefile.in +++ b/charts.d/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,9 +36,11 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = charts.d +DIST_COMMON = $(dist_charts_DATA) $(dist_charts_SCRIPTS) \ + $(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 \ @@ -101,8 +49,6 @@ 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_charts_SCRIPTS) \ - $(dist_charts_DATA) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -136,32 +82,12 @@ am__uninstall_files_from_dir = { \ } am__installdirs = "$(DESTDIR)$(chartsdir)" "$(DESTDIR)$(chartsdir)" SCRIPTS = $(dist_charts_SCRIPTS) -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 = SOURCES = DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac DATA = $(dist_charts_DATA) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -185,7 +111,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@ @@ -199,6 +129,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@ @@ -301,6 +235,10 @@ webdir = @webdir@ # MAINTAINERCLEANFILES = $(srcdir)/Makefile.in dist_charts_SCRIPTS = \ + $(NULL) + +dist_charts_DATA = \ + README.md \ ap.chart.sh \ apcupsd.chart.sh \ apache.chart.sh \ @@ -322,10 +260,6 @@ dist_charts_SCRIPTS = \ tomcat.chart.sh \ $(NULL) -dist_charts_DATA = \ - README.md \ - $(NULL) - all: all-am .SUFFIXES: @@ -341,6 +275,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu charts.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu charts.d/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -360,11 +295,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_chartsSCRIPTS: $(dist_charts_SCRIPTS) @$(NORMAL_INSTALL) + test -z "$(chartsdir)" || $(MKDIR_P) "$(DESTDIR)$(chartsdir)" @list='$(dist_charts_SCRIPTS)'; test -n "$(chartsdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(chartsdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(chartsdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ @@ -395,11 +327,8 @@ uninstall-dist_chartsSCRIPTS: dir='$(DESTDIR)$(chartsdir)'; $(am__uninstall_files_from_dir) install-dist_chartsDATA: $(dist_charts_DATA) @$(NORMAL_INSTALL) + test -z "$(chartsdir)" || $(MKDIR_P) "$(DESTDIR)$(chartsdir)" @list='$(dist_charts_DATA)'; test -n "$(chartsdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(chartsdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(chartsdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -414,11 +343,11 @@ uninstall-dist_chartsDATA: @list='$(dist_charts_DATA)'; test -n "$(chartsdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(chartsdir)'; $(am__uninstall_files_from_dir) -tags TAGS: +tags: TAGS +TAGS: -ctags CTAGS: - -cscope cscopelist: +ctags: CTAGS +CTAGS: distdir: $(DISTFILES) @@ -557,21 +486,18 @@ uninstall-am: uninstall-dist_chartsDATA uninstall-dist_chartsSCRIPTS .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ - ctags-am distclean distclean-generic distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dist_chartsDATA \ - install-dist_chartsSCRIPTS 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-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ - pdf-am ps ps-am tags-am uninstall uninstall-am \ +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-dist_chartsDATA install-dist_chartsSCRIPTS 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-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am \ uninstall-dist_chartsDATA uninstall-dist_chartsSCRIPTS -.PRECIOUS: Makefile - # 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/charts.d/ap.chart.sh b/charts.d/ap.chart.sh old mode 100755 new mode 100644 index 0e85c486d..ce2eefc9f --- a/charts.d/ap.chart.sh +++ b/charts.d/ap.chart.sh @@ -16,7 +16,7 @@ declare -A ap_devs=() # _check is called once, to find out if this chart should be enabled or not ap_check() { require_cmd iw || return 1 - + local ev=$(run iw dev | awk ' BEGIN { i = ""; @@ -77,20 +77,19 @@ DIMENSION retries 'tx retries' incremental 1 1 DIMENSION failures 'tx failures' incremental -1 1 CHART ap_signal.${dev} '' "Average Signal for ${ssid} on ${dev}" "dBm" ${dev} ap.signal line $((ap_priority + 5)) $ap_update_every -DIMENSION signal 'average signal' absolute 1 1 +DIMENSION signal 'average signal' absolute 1 1000 CHART ap_bitrate.${dev} '' "Bitrate for ${ssid} on ${dev}" "Mbps" ${dev} ap.bitrate line $((ap_priority + 6)) $ap_update_every DIMENSION receive '' absolute 1 1000 DIMENSION transmit '' absolute -1 1000 DIMENSION expected 'expected throughput' absolute 1 1000 EOF - done return 0 } -# _update is called continiously, to collect the values +# _update is called continuously, to collect the values ap_update() { # the first argument to this function is the microseconds since last update # pass this parameter to the BEGIN statement (see bellow). @@ -101,66 +100,81 @@ ap_update() { for dev in "${!ap_devs[@]}" do - iw ${dev} station dump |\ - awk " - BEGIN { - c = 0; - rb = 0; - tb = 0; - rp = 0; - tp = 0; - tr = 0; - tf = 0; - tt = 0; - rt = 0; - s = 0; - g = 0; - e = 0; - } - /^Station/ { c++; } - /^[ \\t]+rx bytes:/ { rb += \$3 } - /^[ \\t]+tx bytes:/ { tb += \$3 } - /^[ \\t]+rx packets:/ { rp += \$3 } - /^[ \\t]+tx packets:/ { tp += \$3 } - /^[ \\t]+tx retries:/ { tr += \$3 } - /^[ \\t]+tx failed:/ { tf += \$3 } - /^[ \\t]+signal:/ { s += \$2; } - /^[ \\t]+rx bitrate:/ { x = \$3; rt += x * 1000; } - /^[ \\t]+tx bitrate:/ { x = \$3; tt += x * 1000; } - /^[ \\t]+expected throughput:(.*)Mbps/ { - x=\$3; - sub(/Mbps/, \"\", x); - e += x * 1000; - } - END { - print \"BEGIN ap_clients.${dev}\" - print \"SET clients = \" c; - print \"END\" - print \"BEGIN ap_bandwidth.${dev}\" - print \"SET received = \" rb; - print \"SET sent = \" tb; - print \"END\" - print \"BEGIN ap_packets.${dev}\" - print \"SET received = \" rp; - print \"SET sent = \" tp; - print \"END\" - print \"BEGIN ap_issues.${dev}\" - print \"SET retries = \" tr; - print \"SET failures = \" tf; - print \"END\" - print \"BEGIN ap_signal.${dev}\" - print \"SET signal = \" s / c; - print \"END\" - - if( c == 0 ) c = 1; - print \"BEGIN ap_bitrate.${dev}\" - print \"SET receive = \" rt / c; - print \"SET transmit = \" tt / c; - print \"SET expected = \" e / c; - print \"END\" - } - " - done + echo + echo "DEVICE ${dev}" + iw ${dev} station dump + done | awk " + function zero_data() { + dev = \"\"; + c = 0; + rb = 0; + tb = 0; + rp = 0; + tp = 0; + tr = 0; + tf = 0; + tt = 0; + rt = 0; + s = 0; + g = 0; + e = 0; + } + function print_device() { + if(dev != \"\" && length(dev) > 0) { + print \"BEGIN ap_clients.\" dev; + print \"SET clients = \" c; + print \"END\"; + print \"BEGIN ap_bandwidth.\" dev; + print \"SET received = \" rb; + print \"SET sent = \" tb; + print \"END\"; + print \"BEGIN ap_packets.\" dev; + print \"SET received = \" rp; + print \"SET sent = \" tp; + print \"END\"; + print \"BEGIN ap_issues.\" dev; + print \"SET retries = \" tr; + print \"SET failures = \" tf; + print \"END\"; + + if( c == 0 ) c = 1; + print \"BEGIN ap_signal.\" dev; + print \"SET signal = \" int(s / c); + print \"END\"; + print \"BEGIN ap_bitrate.\" dev; + print \"SET receive = \" int(rt / c); + print \"SET transmit = \" int(tt / c); + print \"SET expected = \" int(e / c); + print \"END\"; + } + zero_data(); + } + BEGIN { + zero_data(); + } + /^DEVICE / { + print_device(); + dev = \$2; + } + /^Station/ { c++; } + /^[ \\t]+rx bytes:/ { rb += \$3; } + /^[ \\t]+tx bytes:/ { tb += \$3; } + /^[ \\t]+rx packets:/ { rp += \$3; } + /^[ \\t]+tx packets:/ { tp += \$3; } + /^[ \\t]+tx retries:/ { tr += \$3; } + /^[ \\t]+tx failed:/ { tf += \$3; } + /^[ \\t]+signal:/ { x = \$2; s += x * 1000; } + /^[ \\t]+rx bitrate:/ { x = \$3; rt += x * 1000; } + /^[ \\t]+tx bitrate:/ { x = \$3; tt += x * 1000; } + /^[ \\t]+expected throughput:(.*)Mbps/ { + x=\$3; + sub(/Mbps/, \"\", x); + e += x * 1000; + } + END { + print_device(); + } + " return 0 } diff --git a/charts.d/apache.chart.sh b/charts.d/apache.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/apcupsd.chart.sh b/charts.d/apcupsd.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/cpu_apps.chart.sh b/charts.d/cpu_apps.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/cpufreq.chart.sh b/charts.d/cpufreq.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/example.chart.sh b/charts.d/example.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/hddtemp.chart.sh b/charts.d/hddtemp.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/load_average.chart.sh b/charts.d/load_average.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/mem_apps.chart.sh b/charts.d/mem_apps.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/mysql.chart.sh b/charts.d/mysql.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/nginx.chart.sh b/charts.d/nginx.chart.sh old mode 100755 new mode 100644 diff --git a/charts.d/nut.chart.sh b/charts.d/nut.chart.sh old mode 100755 new mode 100644 index e0b1b4cf9..6137639f9 --- a/charts.d/nut.chart.sh +++ b/charts.d/nut.chart.sh @@ -2,7 +2,7 @@ # netdata # real-time performance and health monitoring, done right! -# (C) 2016 Costa Tsaousis +# (C) 2016-2017 Costa Tsaousis # GPL v3+ # @@ -13,8 +13,13 @@ nut_ups= # how frequently to collect UPS data nut_update_every=2 +# how much time in seconds, to wait for nut to respond nut_timeout=2 +# set this to 1, to enable another chart showing the number +# of UPS clients connected to upsd +nut_clients_chart=0 + # the priority of nut related to other charts nut_priority=90000 @@ -26,6 +31,12 @@ nut_get_all() { nut_get() { run -t $nut_timeout upsc "$1" + + if [ "${nut_clients_chart}" -eq "1" ] + then + printf "ups.connected_clients: " + run -t $nut_timeout upsc -c "$1" | wc -l + fi } nut_check() { @@ -97,6 +108,15 @@ DIMENSION load load absolute 1 100 CHART nut_$x.temp '' "UPS Temperature" "temperature" ups nut.temperature line $((nut_priority + 7)) $nut_update_every DIMENSION temp temp absolute 1 100 EOF + + if [ "${nut_clients_chart}" = "1" ] + then + cat <. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# This file is maintained in Automake, please report -# bugs to or send patches to -# . - -nl=' -' - -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent tools from complaining about whitespace usage. -IFS=" "" $nl" - -file_conv= - -# func_file_conv build_file lazy -# Convert a $build file to $host form and store it in $file -# Currently only supports Windows hosts. If the determined conversion -# type is listed in (the comma separated) LAZY, no conversion will -# take place. -func_file_conv () -{ - file=$1 - case $file in - / | /[!/]*) # absolute file, and not a UNC file - if test -z "$file_conv"; then - # lazily determine how to convert abs files - case `uname -s` in - MINGW*) - file_conv=mingw - ;; - CYGWIN*) - file_conv=cygwin - ;; - *) - file_conv=wine - ;; - esac - fi - case $file_conv/,$2, in - *,$file_conv,*) - ;; - mingw/*) - file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` - ;; - cygwin/*) - file=`cygpath -m "$file" || echo "$file"` - ;; - wine/*) - file=`winepath -w "$file" || echo "$file"` - ;; - esac - ;; - esac -} - -# func_cl_dashL linkdir -# Make cl look for libraries in LINKDIR -func_cl_dashL () -{ - func_file_conv "$1" - if test -z "$lib_path"; then - lib_path=$file - else - lib_path="$lib_path;$file" - fi - linker_opts="$linker_opts -LIBPATH:$file" -} - -# func_cl_dashl library -# Do a library search-path lookup for cl -func_cl_dashl () -{ - lib=$1 - found=no - save_IFS=$IFS - IFS=';' - for dir in $lib_path $LIB - do - IFS=$save_IFS - if $shared && test -f "$dir/$lib.dll.lib"; then - found=yes - lib=$dir/$lib.dll.lib - break - fi - if test -f "$dir/$lib.lib"; then - found=yes - lib=$dir/$lib.lib - break - fi - if test -f "$dir/lib$lib.a"; then - found=yes - lib=$dir/lib$lib.a - break - fi - done - IFS=$save_IFS - - if test "$found" != yes; then - lib=$lib.lib - fi -} - -# func_cl_wrapper cl arg... -# Adjust compile command to suit cl -func_cl_wrapper () -{ - # Assume a capable shell - lib_path= - shared=: - linker_opts= - for arg - do - if test -n "$eat"; then - eat= - else - case $1 in - -o) - # configure might choose to run compile as 'compile cc -o foo foo.c'. - eat=1 - case $2 in - *.o | *.[oO][bB][jJ]) - func_file_conv "$2" - set x "$@" -Fo"$file" - shift - ;; - *) - func_file_conv "$2" - set x "$@" -Fe"$file" - shift - ;; - esac - ;; - -I) - eat=1 - func_file_conv "$2" mingw - set x "$@" -I"$file" - shift - ;; - -I*) - func_file_conv "${1#-I}" mingw - set x "$@" -I"$file" - shift - ;; - -l) - eat=1 - func_cl_dashl "$2" - set x "$@" "$lib" - shift - ;; - -l*) - func_cl_dashl "${1#-l}" - set x "$@" "$lib" - shift - ;; - -L) - eat=1 - func_cl_dashL "$2" - ;; - -L*) - func_cl_dashL "${1#-L}" - ;; - -static) - shared=false - ;; - -Wl,*) - arg=${1#-Wl,} - save_ifs="$IFS"; IFS=',' - for flag in $arg; do - IFS="$save_ifs" - linker_opts="$linker_opts $flag" - done - IFS="$save_ifs" - ;; - -Xlinker) - eat=1 - linker_opts="$linker_opts $2" - ;; - -*) - set x "$@" "$1" - shift - ;; - *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) - func_file_conv "$1" - set x "$@" -Tp"$file" - shift - ;; - *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) - func_file_conv "$1" mingw - set x "$@" "$file" - shift - ;; - *) - set x "$@" "$1" - shift - ;; - esac - fi - shift - done - if test -n "$linker_opts"; then - linker_opts="-link$linker_opts" - fi - exec "$@" $linker_opts - exit 1 -} - -eat= - -case $1 in - '') - echo "$0: No command. Try '$0 --help' for more information." 1>&2 - exit 1; - ;; - -h | --h*) - cat <<\EOF -Usage: compile [--help] [--version] PROGRAM [ARGS] - -Wrapper for compilers which do not understand '-c -o'. -Remove '-o dest.o' from ARGS, run PROGRAM with the remaining -arguments, and rename the output as expected. - -If you are trying to build a whole package this is not the -right script to run: please start by reading the file 'INSTALL'. - -Report bugs to . -EOF - exit $? - ;; - -v | --v*) - echo "compile $scriptversion" - exit $? - ;; - cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) - func_cl_wrapper "$@" # Doesn't return... - ;; -esac - -ofile= -cfile= - -for arg -do - if test -n "$eat"; then - eat= - else - case $1 in - -o) - # configure might choose to run compile as 'compile cc -o foo foo.c'. - # So we strip '-o arg' only if arg is an object. - eat=1 - case $2 in - *.o | *.obj) - ofile=$2 - ;; - *) - set x "$@" -o "$2" - shift - ;; - esac - ;; - *.c) - cfile=$1 - set x "$@" "$1" - shift - ;; - *) - set x "$@" "$1" - shift - ;; - esac - fi - shift -done - -if test -z "$ofile" || test -z "$cfile"; then - # If no '-o' option was seen then we might have been invoked from a - # pattern rule where we don't need one. That is ok -- this is a - # normal compilation that the losing compiler can handle. If no - # '.c' file was seen then we are probably linking. That is also - # ok. - exec "$@" -fi - -# Name of file we expect compiler to create. -cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` - -# Create the lock directory. -# Note: use '[/\\:.-]' here to ensure that we don't use the same name -# that we are using for the .o file. Also, base the name on the expected -# object file name, since that is what matters with a parallel build. -lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d -while true; do - if mkdir "$lockdir" >/dev/null 2>&1; then - break - fi - sleep 1 -done -# FIXME: race condition here if user kills between mkdir and trap. -trap "rmdir '$lockdir'; exit 1" 1 2 15 - -# Run the compile. -"$@" -ret=$? - -if test -f "$cofile"; then - test "$cofile" = "$ofile" || mv "$cofile" "$ofile" -elif test -f "${cofile}bj"; then - test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" -fi - -rmdir "$lockdir" -exit $ret - -# Local Variables: -# mode: shell-script -# sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" -# time-stamp-end: "; # UTC" -# End: diff --git a/conf.d/Makefile.am b/conf.d/Makefile.am index b725e249e..efe1f2a6e 100644 --- a/conf.d/Makefile.am +++ b/conf.d/Makefile.am @@ -11,6 +11,7 @@ dist_config_DATA = \ python.d.conf \ health_alarm_notify.conf \ health_email_recipients.conf \ + stream.conf \ $(NULL) nodeconfigdir=$(configdir)/node.d @@ -33,16 +34,16 @@ dist_pythonconfig_DATA = \ python.d/exim.conf \ python.d/fail2ban.conf \ python.d/freeradius.conf \ - python.d/gunicorn_log.conf \ python.d/haproxy.conf \ python.d/hddtemp.conf \ python.d/ipfs.conf \ python.d/isc_dhcpd.conf \ python.d/mdstat.conf \ python.d/memcached.conf \ + python.d/mongodb.conf \ python.d/mysql.conf \ python.d/nginx.conf \ - python.d/nginx_log.conf \ + python.d/nsd.conf \ python.d/ovpn_status_log.conf \ python.d/phpfpm.conf \ python.d/postfix.conf \ @@ -51,43 +52,54 @@ dist_pythonconfig_DATA = \ python.d/retroshare.conf \ python.d/sensors.conf \ python.d/squid.conf \ + python.d/smartd_log.conf \ python.d/tomcat.conf \ python.d/varnish.conf \ + python.d/web_log.conf \ $(NULL) healthconfigdir=$(configdir)/health.d + dist_healthconfig_DATA = \ health.d/apache.conf \ health.d/backend.conf \ health.d/bind_rndc.conf \ - health.d/cpu.conf \ - health.d/disks.conf \ health.d/elasticsearch.conf \ - health.d/entropy.conf \ + health.d/fping.conf \ health.d/haproxy.conf \ - health.d/ipc.conf \ health.d/ipfs.conf \ + health.d/ipmi.conf \ health.d/isc_dhcpd.conf \ health.d/mdstat.conf \ health.d/memcached.conf \ - health.d/memory.conf \ health.d/mysql.conf \ health.d/named.conf \ - health.d/net.conf \ - health.d/netfilter.conf \ health.d/nginx.conf \ health.d/postgres.conf \ - health.d/qos.conf \ - health.d/ram.conf \ health.d/redis.conf \ health.d/retroshare.conf \ - health.d/softnet.conf \ health.d/squid.conf \ + health.d/varnish.conf \ + health.d/web_log.conf \ + $(NULL) + +if LINUX +dist_healthconfig_DATA += \ + health.d/cpu.conf \ + health.d/disks.conf \ + health.d/entropy.conf \ + health.d/ipc.conf \ + health.d/memory.conf \ + health.d/net.conf \ + health.d/netfilter.conf \ + health.d/qos.conf \ + health.d/ram.conf \ + health.d/softnet.conf \ health.d/swap.conf \ health.d/tcp_resets.conf \ health.d/udp_errors.conf \ - health.d/varnish.conf \ $(NULL) +endif LINUX chartsconfigdir=$(configdir)/charts.d dist_chartsconfig_DATA = \ diff --git a/conf.d/Makefile.in b/conf.d/Makefile.in index 344f1c416..fb05396fb 100644 --- a/conf.d/Makefile.in +++ b/conf.d/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. @@ -15,61 +16,6 @@ @SET_MAKE@ 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@ @@ -88,10 +34,30 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ +@LINUX_TRUE@am__append_1 = \ +@LINUX_TRUE@ health.d/cpu.conf \ +@LINUX_TRUE@ health.d/disks.conf \ +@LINUX_TRUE@ health.d/entropy.conf \ +@LINUX_TRUE@ health.d/ipc.conf \ +@LINUX_TRUE@ health.d/memory.conf \ +@LINUX_TRUE@ health.d/net.conf \ +@LINUX_TRUE@ health.d/netfilter.conf \ +@LINUX_TRUE@ health.d/qos.conf \ +@LINUX_TRUE@ health.d/ram.conf \ +@LINUX_TRUE@ health.d/softnet.conf \ +@LINUX_TRUE@ health.d/swap.conf \ +@LINUX_TRUE@ health.d/tcp_resets.conf \ +@LINUX_TRUE@ health.d/udp_errors.conf \ +@LINUX_TRUE@ $(NULL) + subdir = conf.d +DIST_COMMON = $(am__dist_healthconfig_DATA_DIST) \ + $(dist_chartsconfig_DATA) $(dist_config_DATA) \ + $(dist_nodeconfig_DATA) $(dist_pythonconfig_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 \ @@ -100,33 +66,12 @@ 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_chartsconfig_DATA) \ - $(dist_config_DATA) $(dist_healthconfig_DATA) \ - $(dist_nodeconfig_DATA) $(dist_pythonconfig_DATA) \ - $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = -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 = SOURCES = DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -157,15 +102,26 @@ am__uninstall_files_from_dir = { \ am__installdirs = "$(DESTDIR)$(chartsconfigdir)" \ "$(DESTDIR)$(configdir)" "$(DESTDIR)$(healthconfigdir)" \ "$(DESTDIR)$(nodeconfigdir)" "$(DESTDIR)$(pythonconfigdir)" +am__dist_healthconfig_DATA_DIST = health.d/apache.conf \ + health.d/backend.conf health.d/bind_rndc.conf \ + health.d/elasticsearch.conf health.d/fping.conf \ + health.d/haproxy.conf health.d/ipfs.conf health.d/ipmi.conf \ + health.d/isc_dhcpd.conf health.d/mdstat.conf \ + health.d/memcached.conf health.d/mysql.conf \ + health.d/named.conf health.d/nginx.conf health.d/postgres.conf \ + health.d/redis.conf health.d/retroshare.conf \ + health.d/squid.conf health.d/varnish.conf \ + health.d/web_log.conf health.d/cpu.conf health.d/disks.conf \ + health.d/entropy.conf health.d/ipc.conf health.d/memory.conf \ + health.d/net.conf health.d/netfilter.conf health.d/qos.conf \ + health.d/ram.conf health.d/softnet.conf health.d/swap.conf \ + health.d/tcp_resets.conf health.d/udp_errors.conf DATA = $(dist_chartsconfig_DATA) $(dist_config_DATA) \ $(dist_healthconfig_DATA) $(dist_nodeconfig_DATA) \ $(dist_pythonconfig_DATA) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -189,7 +145,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@ @@ -203,6 +163,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@ @@ -312,6 +276,7 @@ dist_config_DATA = \ python.d.conf \ health_alarm_notify.conf \ health_email_recipients.conf \ + stream.conf \ $(NULL) nodeconfigdir = $(configdir)/node.d @@ -334,16 +299,16 @@ dist_pythonconfig_DATA = \ python.d/exim.conf \ python.d/fail2ban.conf \ python.d/freeradius.conf \ - python.d/gunicorn_log.conf \ python.d/haproxy.conf \ python.d/hddtemp.conf \ python.d/ipfs.conf \ python.d/isc_dhcpd.conf \ python.d/mdstat.conf \ python.d/memcached.conf \ + python.d/mongodb.conf \ python.d/mysql.conf \ python.d/nginx.conf \ - python.d/nginx_log.conf \ + python.d/nsd.conf \ python.d/ovpn_status_log.conf \ python.d/phpfpm.conf \ python.d/postfix.conf \ @@ -352,44 +317,23 @@ dist_pythonconfig_DATA = \ python.d/retroshare.conf \ python.d/sensors.conf \ python.d/squid.conf \ + python.d/smartd_log.conf \ python.d/tomcat.conf \ python.d/varnish.conf \ + python.d/web_log.conf \ $(NULL) healthconfigdir = $(configdir)/health.d -dist_healthconfig_DATA = \ - health.d/apache.conf \ - health.d/backend.conf \ - health.d/bind_rndc.conf \ - health.d/cpu.conf \ - health.d/disks.conf \ - health.d/elasticsearch.conf \ - health.d/entropy.conf \ - health.d/haproxy.conf \ - health.d/ipc.conf \ - health.d/ipfs.conf \ - health.d/isc_dhcpd.conf \ - health.d/mdstat.conf \ - health.d/memcached.conf \ - health.d/memory.conf \ - health.d/mysql.conf \ - health.d/named.conf \ - health.d/net.conf \ - health.d/netfilter.conf \ - health.d/nginx.conf \ - health.d/postgres.conf \ - health.d/qos.conf \ - health.d/ram.conf \ - health.d/redis.conf \ - health.d/retroshare.conf \ - health.d/softnet.conf \ - health.d/squid.conf \ - health.d/swap.conf \ - health.d/tcp_resets.conf \ - health.d/udp_errors.conf \ - health.d/varnish.conf \ - $(NULL) - +dist_healthconfig_DATA = health.d/apache.conf health.d/backend.conf \ + health.d/bind_rndc.conf health.d/elasticsearch.conf \ + health.d/fping.conf health.d/haproxy.conf health.d/ipfs.conf \ + health.d/ipmi.conf health.d/isc_dhcpd.conf \ + health.d/mdstat.conf health.d/memcached.conf \ + health.d/mysql.conf health.d/named.conf health.d/nginx.conf \ + health.d/postgres.conf health.d/redis.conf \ + health.d/retroshare.conf health.d/squid.conf \ + health.d/varnish.conf health.d/web_log.conf $(NULL) \ + $(am__append_1) chartsconfigdir = $(configdir)/charts.d dist_chartsconfig_DATA = \ charts.d/apache.conf \ @@ -428,6 +372,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu conf.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu conf.d/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -447,11 +392,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_chartsconfigDATA: $(dist_chartsconfig_DATA) @$(NORMAL_INSTALL) + test -z "$(chartsconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(chartsconfigdir)" @list='$(dist_chartsconfig_DATA)'; test -n "$(chartsconfigdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(chartsconfigdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(chartsconfigdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -468,11 +410,8 @@ uninstall-dist_chartsconfigDATA: dir='$(DESTDIR)$(chartsconfigdir)'; $(am__uninstall_files_from_dir) install-dist_configDATA: $(dist_config_DATA) @$(NORMAL_INSTALL) + test -z "$(configdir)" || $(MKDIR_P) "$(DESTDIR)$(configdir)" @list='$(dist_config_DATA)'; test -n "$(configdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(configdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(configdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -489,11 +428,8 @@ uninstall-dist_configDATA: dir='$(DESTDIR)$(configdir)'; $(am__uninstall_files_from_dir) install-dist_healthconfigDATA: $(dist_healthconfig_DATA) @$(NORMAL_INSTALL) + test -z "$(healthconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(healthconfigdir)" @list='$(dist_healthconfig_DATA)'; test -n "$(healthconfigdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(healthconfigdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(healthconfigdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -510,11 +446,8 @@ uninstall-dist_healthconfigDATA: dir='$(DESTDIR)$(healthconfigdir)'; $(am__uninstall_files_from_dir) install-dist_nodeconfigDATA: $(dist_nodeconfig_DATA) @$(NORMAL_INSTALL) + test -z "$(nodeconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(nodeconfigdir)" @list='$(dist_nodeconfig_DATA)'; test -n "$(nodeconfigdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(nodeconfigdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(nodeconfigdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -531,11 +464,8 @@ uninstall-dist_nodeconfigDATA: dir='$(DESTDIR)$(nodeconfigdir)'; $(am__uninstall_files_from_dir) install-dist_pythonconfigDATA: $(dist_pythonconfig_DATA) @$(NORMAL_INSTALL) + test -z "$(pythonconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(pythonconfigdir)" @list='$(dist_pythonconfig_DATA)'; test -n "$(pythonconfigdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pythonconfigdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pythonconfigdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -550,11 +480,11 @@ uninstall-dist_pythonconfigDATA: @list='$(dist_pythonconfig_DATA)'; test -n "$(pythonconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pythonconfigdir)'; $(am__uninstall_files_from_dir) -tags TAGS: +tags: TAGS +TAGS: -ctags CTAGS: - -cscope cscopelist: +ctags: CTAGS +CTAGS: distdir: $(DISTFILES) @@ -697,23 +627,21 @@ uninstall-am: uninstall-dist_chartsconfigDATA \ .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ - ctags-am distclean distclean-generic distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dist_chartsconfigDATA \ - install-dist_configDATA install-dist_healthconfigDATA \ - install-dist_nodeconfigDATA install-dist_pythonconfigDATA \ - 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-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags-am \ - uninstall uninstall-am uninstall-dist_chartsconfigDATA \ - uninstall-dist_configDATA uninstall-dist_healthconfigDATA \ - uninstall-dist_nodeconfigDATA uninstall-dist_pythonconfigDATA - -.PRECIOUS: Makefile +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-dist_chartsconfigDATA install-dist_configDATA \ + install-dist_healthconfigDATA install-dist_nodeconfigDATA \ + install-dist_pythonconfigDATA 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-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am uninstall uninstall-am \ + uninstall-dist_chartsconfigDATA uninstall-dist_configDATA \ + uninstall-dist_healthconfigDATA uninstall-dist_nodeconfigDATA \ + uninstall-dist_pythonconfigDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/conf.d/apps_groups.conf b/conf.d/apps_groups.conf index e2836877c..4c5171b3d 100644 --- a/conf.d/apps_groups.conf +++ b/conf.d/apps_groups.conf @@ -73,10 +73,12 @@ netdata: netdata # netdata known plugins # plugins not defined here will be accumulated in netdata, above apps.plugin: apps.plugin +freeipmi.plugin: freeipmi.plugin charts.d.plugin: *charts.d.plugin* node.d.plugin: *node.d.plugin* python.d.plugin: *python.d.plugin* tc-qos-helper: *tc-qos-helper.sh* +fping: fping # ----------------------------------------------------------------------------- # authentication/authorization related servers @@ -106,11 +108,12 @@ nosql: mongod redis* memcached email: dovecot imapd pop3d amavis* master zmstat* zmmailboxdmgr qmgr oqmgr # ----------------------------------------------------------------------------- -# networking and VPN servers +# network, routing, VPN ppp: ppp* vpn: openvpn pptp* cjdroute wifi: hostapd wpa_supplicant +routing: ospfd* ospf6d* bgpd isisd ripd ripngd pimd ldpd zebra vtysh bird* # ----------------------------------------------------------------------------- # high availability and balancers diff --git a/conf.d/charts.d/nut.conf b/conf.d/charts.d/nut.conf index 2844849de..a836692d8 100644 --- a/conf.d/charts.d/nut.conf +++ b/conf.d/charts.d/nut.conf @@ -2,7 +2,7 @@ # netdata # real-time performance and health monitoring, done right! -# (C) 2016 Costa Tsaousis +# (C) 2016-2017 Costa Tsaousis # GPL v3+ # a space separated list of UPS names @@ -12,6 +12,10 @@ # how much time in seconds, to wait for nut to respond #nut_timeout=2 +# set this to 1, to enable another chart showing the number +# of UPS clients connected to upsd +#nut_clients_chart=1 + # the data collection frequency # if unset, will inherit the netdata update frequency #nut_update_every=2 diff --git a/conf.d/health.d/cpu.conf b/conf.d/health.d/cpu.conf index 60f494d70..30a714097 100644 --- a/conf.d/health.d/cpu.conf +++ b/conf.d/health.d/cpu.conf @@ -1,13 +1,13 @@ template: 10min_cpu_usage on: system.cpu - lookup: average -10m unaligned of user,system,nice,softirq,irq,guest,guest_nice + lookup: average -10m unaligned of user,system,softirq,irq,guest units: % every: 1m warn: $this > (($status >= $WARNING) ? (75) : (85)) crit: $this > (($status == $CRITICAL) ? (85) : (95)) delay: down 15m multiplier 1.5 max 1h - info: average cpu utilization for the last 10 minutes + info: average cpu utilization for the last 10 minutes (excluding iowait, nice and steal) to: sysadmin template: 10min_cpu_iowait diff --git a/conf.d/health.d/disks.conf b/conf.d/health.d/disks.conf index 0549bac26..9548f9ee0 100644 --- a/conf.d/health.d/disks.conf +++ b/conf.d/health.d/disks.conf @@ -1,33 +1,3 @@ -# ----------------------------------------------------------------------------- -# make sure we collect values for each disk - -# for mount points -template: disk_space_last_collected_secs - on: disk.space -families: * - calc: $now - $last_collected_t - units: seconds ago - every: 10s - warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) - delay: down 5m multiplier 1.5 max 1h - info: number of seconds since the last successful data collection of the mount point - to: sysadmin - -# for block devices -template: disk_last_collected_secs - on: disk.io -families: * - calc: $now - $last_collected_t - units: seconds ago - every: 10s - warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) - delay: down 5m multiplier 1.5 max 1h - info: number of seconds since the last successful data collection of the block device - to: sysadmin - - # ----------------------------------------------------------------------------- # low disk space @@ -88,7 +58,7 @@ families: * template: out_of_disk_space_time on: disk.space families: * - calc: ($disk_fill_rate > 0) ? ($avail / $disk_fill_rate) : (0) + calc: ($disk_fill_rate > 0) ? ($avail / $disk_fill_rate) : (inf) units: hours every: 10s warn: $this > 0 and $this < (($status >= $WARNING) ? (48) : (8)) diff --git a/conf.d/health.d/fping.conf b/conf.d/health.d/fping.conf new file mode 100644 index 000000000..69251b182 --- /dev/null +++ b/conf.d/health.d/fping.conf @@ -0,0 +1,53 @@ + +template: fping_last_collected_secs +families: * + on: fping.latency + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + delay: down 5m multiplier 1.5 max 1h + info: number of seconds since the last successful data collection + to: sysadmin + +template: host_reachable +families: * + on: fping.latency + calc: $average != nan + units: up/down + every: 10s + crit: $this == 0 + info: states if the remote host is reachable + delay: down 30m multiplier 1.5 max 2h + to: sysadmin + +template: host_latency +families: * + on: fping.latency + lookup: average -10s unaligned of average + units: ms + every: 10s + green: 300 + red: 1000 + warn: $this > $green OR $max > $red + crit: $this > $red + info: average round trip delay during the last 10 seconds + delay: down 30m multiplier 1.5 max 2h + to: sysadmin + +template: packet_loss +families: * + on: fping.quality + lookup: average -10m unaligned of returned + calc: 100 - $this + green: 1 + red: 10 + units: % + every: 10s + warn: $this > $green + crit: $this > $red + info: packet loss percentage + delay: down 30m multiplier 1.5 max 2h + to: sysadmin + diff --git a/conf.d/health.d/ipmi.conf b/conf.d/health.d/ipmi.conf new file mode 100644 index 000000000..c25581964 --- /dev/null +++ b/conf.d/health.d/ipmi.conf @@ -0,0 +1,20 @@ + alarm: ipmi_sensors_states + on: ipmi.sensors_states + calc: $warning + $critical + units: sensors + every: 10s + warn: $this > 0 + crit: $critical > 0 + delay: up 5m down 15m multiplier 1.5 max 1h + info: the number IPMI sensors in non-nominal state + to: sysadmin + + alarm: ipmi_events + on: ipmi.events + calc: $events + units: events + every: 10s + warn: $this > 0 + delay: up 5m down 15m multiplier 1.5 max 1h + info: the number of events in the IPMI System Event Log (SEL) + to: sysadmin diff --git a/conf.d/health.d/memcached.conf b/conf.d/health.d/memcached.conf index 7917e36af..d248ef57a 100644 --- a/conf.d/health.d/memcached.conf +++ b/conf.d/health.d/memcached.conf @@ -42,7 +42,7 @@ template: cache_fill_rate template: out_of_cache_space_time on: memcached.cache - calc: ($cache_fill_rate > 0) ? ($available / $cache_fill_rate) : (0) + calc: ($cache_fill_rate > 0) ? ($available / $cache_fill_rate) : (inf) units: hours every: 10s warn: $this > 0 and $this < (($status >= $WARNING) ? (48) : (8)) diff --git a/conf.d/health.d/mysql.conf b/conf.d/health.d/mysql.conf index 78773e5b5..1eeb993f0 100644 --- a/conf.d/health.d/mysql.conf +++ b/conf.d/health.d/mysql.conf @@ -49,7 +49,7 @@ template: mysql_10s_table_locks_waited template: mysql_10s_waited_locks_ratio on: mysql.table_locks - calc: ($mysql_10s_table_locks_waited * 100) / ($mysql_10s_table_locks_waited + $mysql_10s_table_locks_immediate) + calc: ( ($mysql_10s_table_locks_waited + $mysql_10s_table_locks_immediate) > 0 ) ? (($mysql_10s_table_locks_waited * 100) / ($mysql_10s_table_locks_waited + $mysql_10s_table_locks_immediate)) : 0 units: % every: 10s warn: $this > (($status >= $WARNING) ? (10) : (25)) @@ -65,7 +65,7 @@ template: mysql_10s_waited_locks_ratio template: mysql_replication on: mysql.slave_status calc: ($sql_running == -1 OR $io_running == -1)?0:1 - units: status + units: ok/failed every: 10s crit: $this == 0 delay: down 5m multiplier 1.5 max 1h diff --git a/conf.d/health.d/net.conf b/conf.d/health.d/net.conf index 924acccc3..0232395ac 100644 --- a/conf.d/health.d/net.conf +++ b/conf.d/health.d/net.conf @@ -1,18 +1,3 @@ -# ----------------------------------------------------------------------------- -# make sure we collect values for each interface - -template: interface_last_collected_secs - on: net.net -families: * - calc: $now - $last_collected_t - units: seconds ago - every: 10s - warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) - crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) - delay: down 5m multiplier 1.5 max 1h - info: number of seconds since the last successful data collection - to: sysadmin - # ----------------------------------------------------------------------------- # dropped packets @@ -116,6 +101,7 @@ families: * units: % warn: $this > (($status >= $WARNING)?(200):(1000)) crit: $this > (($status >= $WARNING)?(1000):(2000)) +options: no-clear-notification info: the % of the rate of received packets in the last 10 seconds, compared to the rate of the last minute - to: silent + to: sysadmin diff --git a/conf.d/health.d/tcp_resets.conf b/conf.d/health.d/tcp_resets.conf index daf24a1cd..49fb1b924 100644 --- a/conf.d/health.d/tcp_resets.conf +++ b/conf.d/health.d/tcp_resets.conf @@ -28,8 +28,9 @@ every: 10s warn: $this > ((($1m_ipv4_tcp_resets_sent < 5)?(5):($1m_ipv4_tcp_resets_sent)) * (($status >= $WARNING) ? (1) : (4))) delay: up 0 down 60m multiplier 1.2 max 2h +options: no-clear-notification info: average TCP RESETS this host is sending, over the last 10 seconds (this can be an indication that a port scan is made, or that a service running on this host has crashed) - to: silent + to: sysadmin # ----------------------------------------------------------------------------- # tcp resets this host receives @@ -48,5 +49,6 @@ every: 10s warn: $this > ((($1m_ipv4_tcp_resets_received < 5)?(5):($1m_ipv4_tcp_resets_received)) * (($status >= $WARNING) ? (1) : (4))) delay: up 0 down 60m multiplier 1.2 max 2h +options: no-clear-notification info: average TCP RESETS this host is receiving, over the last 10 seconds (this can be an indication that a service this host needs, has crashed) - to: silent + to: sysadmin diff --git a/conf.d/health.d/web_log.conf b/conf.d/health.d/web_log.conf new file mode 100644 index 000000000..c668959f5 --- /dev/null +++ b/conf.d/health.d/web_log.conf @@ -0,0 +1,161 @@ + +# make sure we can collect web log data + +template: last_collected_secs + on: web_log.response_codes +families: * + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + delay: down 5m multiplier 1.5 max 1h + info: number of seconds since the last successful data collection + to: webmaster + + +# ----------------------------------------------------------------------------- +# high level response code alarms + +# the following alarms trigger only when there are enough data. +# we assume there are enough data when: +# +# $1m_requests > 120 +# +# i.e. when there are at least 120 requests during the last minute + +template: 1m_requests + on: web_log.response_statuses +families: * + lookup: sum -1m unaligned + calc: ($this == 0)?(1):($this) + units: requests + every: 10s + info: the sum of all HTTP requests over the last minute + +template: 1m_successful + on: web_log.response_statuses +families: * + lookup: sum -1m unaligned of successful_requests + calc: $this * 100 / $1m_requests + units: % + every: 10s + warn: ($1m_requests > 120) ? ($this < (($status >= $WARNING ) ? ( 95 ) : ( 85 )) ) : ( 0 ) + crit: ($1m_requests > 120) ? ($this < (($status == $CRITICAL) ? ( 85 ) : ( 75 )) ) : ( 0 ) + delay: up 2m down 15m multiplier 1.5 max 1h + info: the ratio of successful HTTP responses (1xx, 2xx, 304) over the last minute + to: webmaster + +template: 1m_redirects + on: web_log.response_statuses +families: * + lookup: sum -1m unaligned of redirects + calc: $this * 100 / $1m_requests + units: % + every: 10s + warn: ($1m_requests > 120) ? ($this > (($status >= $WARNING ) ? ( 1 ) : ( 20 )) ) : ( 0 ) + crit: ($1m_requests > 120) ? ($this > (($status == $CRITICAL) ? ( 20 ) : ( 30 )) ) : ( 0 ) + delay: up 2m down 15m multiplier 1.5 max 1h + info: the ratio of HTTP redirects (3xx except 304) over the last minute + to: webmaster + +template: 1m_bad_requests + on: web_log.response_statuses +families: * + lookup: sum -1m unaligned of bad_requests + calc: $this * 100 / $1m_requests + units: % + every: 10s + warn: ($1m_requests > 120) ? ($this > (($status >= $WARNING) ? ( 10 ) : ( 30 )) ) : ( 0 ) + crit: ($1m_requests > 120) ? ($this > (($status == $CRITICAL) ? ( 30 ) : ( 50 )) ) : ( 0 ) + delay: up 2m down 15m multiplier 1.5 max 1h + info: the ratio of HTTP bad requests (4xx) over the last minute + to: webmaster + +template: 1m_internal_errors + on: web_log.response_statuses +families: * + lookup: sum -1m unaligned of server_errors + calc: $this * 100 / $1m_requests + units: % + every: 10s + warn: ($1m_requests > 120) ? ($this > (($status >= $WARNING) ? ( 1 ) : ( 2 )) ) : ( 0 ) + crit: ($1m_requests > 120) ? ($this > (($status == $CRITICAL) ? ( 2 ) : ( 5 )) ) : ( 0 ) + delay: up 2m down 15m multiplier 1.5 max 1h + info: the ratio of HTTP internal server errors (5xx), over the last minute + to: webmaster + + +# ----------------------------------------------------------------------------- +# web slow + +# the following alarms trigger only when there are enough data. +# we assume there are enough data when: +# +# $1m_requests > 120 +# +# i.e. when there are at least 120 requests during the last minute + +template: 10m_response_time + on: web_log.response_time +families: * + lookup: average -10m unaligned of avg + units: ms + every: 30s + info: the average time to respond to HTTP requests, over the last 10 minutes + +template: web_slow + on: web_log.response_time +families: * + lookup: average -1m unaligned of avg + units: ms + every: 10s + green: 500 + red: 1000 + warn: ($1m_requests > 120) ? ($this > $green && $this > ($10m_response_time * 2) ) : ( 0 ) + crit: ($1m_requests > 120) ? ($this > $red && $this > ($10m_response_time * 4) ) : ( 0 ) + delay: down 15m multiplier 1.5 max 1h + info: the average time to respond to HTTP requests, over the last 1 minute + to: webmaster + +# ----------------------------------------------------------------------------- +# web too many or too few requests + +# the following alarms trigger only when there are enough data. +# we assume there are enough data when: +# +# $5m_successful_old > 120 +# +# i.e. when there were at least 120 requests during the 5 minutes starting +# at -10m and ending at -5m + +template: 5m_successful_old + on: web_log.response_statuses +families: * + lookup: average -5m at -5m unaligned of successful_requests + units: requests/s + every: 30s + info: average rate of successful HTTP requests over the last 5 minutes + +template: 5m_successful + on: web_log.response_statuses +families: * + lookup: average -5m unaligned of successful_requests + units: requests/s + every: 30s + info: average successful HTTP requests over the last 5 minutes + +template: 5m_requests_ratio + on: web_log.response_codes +families: * + calc: ($5m_successful_old > 0)?($5m_successful * 100 / $5m_successful_old):(100) + units: % + every: 30s + warn: ($5m_successful_old > 120) ? ($this > 200 OR $this < 50) : (0) + crit: ($5m_successful_old > 120) ? ($this > 400 OR $this < 25) : (0) + delay: down 15m multiplier 1.5 max 1h +options: no-clear-notification + info: the percentage of successful web requests over the last 5 minutes, \ + compared with the previous 5 minutes + to: webmaster + diff --git a/conf.d/health_alarm_notify.conf b/conf.d/health_alarm_notify.conf index b838e8024..23776b96a 100644 --- a/conf.d/health_alarm_notify.conf +++ b/conf.d/health_alarm_notify.conf @@ -7,6 +7,7 @@ # - e-mails (using the sendmail command), # - push notifications to your mobile phone (pushover.net), # - messages to your slack team (slack.com), +# - messages to your discord guild (discordapp.com), # - messages to your telegram chat / group chat (telegram.org) # - sms messages to your cell phone or any sms enabled device (twilio.com) # - sms messages to your cell phone or any sms enabled device (messagebird.com) @@ -22,7 +23,7 @@ # proxy configuration # # If you need to send curl based notifications (pushover, pushbullet, slack, -# telegram) via a proxy, set these to your proxy address: +# discord, telegram) via a proxy, set these to your proxy address: #export http_proxy="http://10.0.0.1:3128/" #export https_proxy="http://10.0.0.1:3128/" @@ -62,6 +63,7 @@ curl="" # - pushover user tokens # - telegram chat ids # - slack channels +# - discord channels # - hipchat rooms # - sms phone numbers # - pagerduty.com (pd) services @@ -75,6 +77,7 @@ curl="" # pushover : "2987343...9437837 8756278...2362736|critical" # telegram : "111827421 112746832|critical" # slack : "alarms disasters|critical" +# discord : "alarms disasters|critical" # twilio : "+15555555555 +17777777777|critical" # messagebird: "+15555555555 +17777777777|critical" # pd : " |critical" @@ -227,6 +230,25 @@ SLACK_WEBHOOK_URL="" DEFAULT_RECIPIENT_SLACK="" +#------------------------------------------------------------------------------ +# discord (discordapp.com) global notification options + +# multiple recipients can be given like this: +# "CHANNEL1 CHANNEL2 ..." + +# enable/disable sending discord notifications +SEND_DISCORD="YES" + +# Create a webhook by following the official documentation - +# https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks +DISCORD_WEBHOOK_URL="" + +# if a role's recipients are not configured, a notification will be send to +# this discord channel (empty = do not send a notification for unconfigured +# roles): +DEFAULT_RECIPIENT_DISCORD="" + + #------------------------------------------------------------------------------ # hipchat global notification options @@ -236,6 +258,9 @@ DEFAULT_RECIPIENT_SLACK="" # enable/disable sending hipchat notifications SEND_HIPCHAT="YES" +# define hipchat server +HIPCHAT_SERVER="api.hipchat.com" + # api.hipchat.com authorization token # Without this, netdata cannot send hipchat notifications. HIPCHAT_AUTH_TOKEN="" @@ -295,6 +320,8 @@ role_recipients_telegram[sysadmin]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[sysadmin]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_discord[sysadmin]="${DEFAULT_RECIPIENT_DISCORD}" + role_recipients_hipchat[sysadmin]="${DEFAULT_RECIPIENT_HIPCHAT}" role_recipients_twilio[sysadmin]="${DEFAULT_RECIPIENT_TWILIO}" @@ -316,6 +343,8 @@ role_recipients_telegram[domainadmin]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[domainadmin]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_discord[domainadmin]="${DEFAULT_RECIPIENT_DISCORD}" + role_recipients_hipchat[domainadmin]="${DEFAULT_RECIPIENT_HIPCHAT}" role_recipients_twilio[domainadmin]="${DEFAULT_RECIPIENT_TWILIO}" @@ -338,6 +367,8 @@ role_recipients_telegram[dba]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[dba]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_discord[dba]="${DEFAULT_RECIPIENT_DISCORD}" + role_recipients_hipchat[dba]="${DEFAULT_RECIPIENT_HIPCHAT}" role_recipients_twilio[dba]="${DEFAULT_RECIPIENT_TWILIO}" @@ -360,6 +391,8 @@ role_recipients_telegram[webmaster]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[webmaster]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_discord[webmaster]="${DEFAULT_RECIPIENT_DISCORD}" + role_recipients_hipchat[webmaster]="${DEFAULT_RECIPIENT_HIPCHAT}" role_recipients_twilio[webmaster]="${DEFAULT_RECIPIENT_TWILIO}" @@ -382,6 +415,8 @@ role_recipients_telegram[proxyadmin]="${DEFAULT_RECIPIENT_TELEGRAM}" role_recipients_slack[proxyadmin]="${DEFAULT_RECIPIENT_SLACK}" +role_recipients_discord[proxyadmin]="${DEFAULT_RECIPIENT_DISCORD}" + role_recipients_hipchat[proxyadmin]="${DEFAULT_RECIPIENT_HIPCHAT}" role_recipients_twilio[proxyadmin]="${DEFAULT_RECIPIENT_TWILIO}" diff --git a/conf.d/node.d/snmp.conf.md b/conf.d/node.d/snmp.conf.md index bae5bf207..6b496f7a8 100644 --- a/conf.d/node.d/snmp.conf.md +++ b/conf.d/node.d/snmp.conf.md @@ -1,341 +1,359 @@ -# SNMP Data Collector - -Using this collector, netdata can collect data from any SNMP device. - -This collector supports: - -- any number of SNMP devices -- each SNMP device can be used to collect data for any number of charts -- each chart may have any number of dimensions -- each SNMP device may have a different update frequency -- each SNMP device will accept one or more batches to report values (you can set `max_request_size` per SNMP server, to control the size of batches). - -The source code of the plugin is [here](https://github.com/firehol/netdata/blob/master/node.d/snmp.node.js). - -## Configuration - -You will need to create the file `/etc/netdata/node.d/snmp.conf` with data like the following. - -In this example: - - - the SNMP device is `10.11.12.8`. - - the SNMP community is `public`. - - we will update the values every 10 seconds (`update_every: 10` under the server `10.11.12.8`). - - we define 2 charts `snmp_switch.bandwidth_port1` and `snmp_switch.bandwidth_port2`, each having 2 dimensions: `in` and `out`. - -```js -{ - "enable_autodetect": false, - "update_every": 5, - "max_request_size": 100, - "servers": [ - { - "hostname": "10.11.12.8", - "community": "public", - "update_every": 10, - "max_request_size": 50, - "options": { "timeout": 10000 }, - "charts": { - "snmp_switch.bandwidth_port1": { - "title": "Switch Bandwidth for port 1", - "units": "kilobits/s", - "type": "area", - "priority": 1, - "family": "ports", - "dimensions": { - "in": { - "oid": "1.3.6.1.2.1.2.2.1.10.1", - "algorithm": "incremental", - "multiplier": 8, - "divisor": 1024 - }, - "out": { - "oid": "1.3.6.1.2.1.2.2.1.16.1", - "algorithm": "incremental", - "multiplier": -8, - "divisor": 1024 - } - } - }, - "snmp_switch.bandwidth_port2": { - "title": "Switch Bandwidth for port 2", - "units": "kilobits/s", - "type": "area", - "priority": 1, - "family": "ports", - "dimensions": { - "in": { - "oid": "1.3.6.1.2.1.2.2.1.10.2", - "algorithm": "incremental", - "multiplier": 8, - "divisor": 1024 - }, - "out": { - "oid": "1.3.6.1.2.1.2.2.1.16.2", - "algorithm": "incremental", - "multiplier": -8, - "divisor": 1024 - } - } - } - } - } - ] -} -``` - -`update_every` is the update frequency for each server, in seconds. - -`max_request_size` limits the maximum number of OIDs that will be requested in a single call. The default is 50. Lower this number of you get `TooBig` errors in netdata error.log. - -`family` sets the name of the submenu of the dashboard each chart will appear under. - -If you need to define many charts using incremental OIDs, you can use something like this: - -This is like the previous, but the option `multiply_range` given, will multiply the current chart from `1` to `24` inclusive, producing 24 charts in total for the 24 ports of the switch `10.11.12.8`. - -Each of the 24 new charts will have its id (1-24) appended at: - -1. its chart unique id, i.e. `snmp_switch.bandwidth_port1` to `snmp_switch.bandwidth_port24` -2. its `title`, i.e. `Switch Bandwidth for port 1` to `Switch Bandwidth for port 24` -3. its `oid` (for all dimensions), i.e. dimension `in` will be `1.3.6.1.2.1.2.2.1.10.1` to `1.3.6.1.2.1.2.2.1.10.24` -3. its priority (which will be incremented for each chart so that the charts will appear on the dashboard in this order) - -```js -{ - "enable_autodetect": false, - "update_every": 10, - "servers": [ - { - "hostname": "10.11.12.8", - "community": "public", - "update_every": 10, - "options": { "timeout": 20000 }, - "charts": { - "snmp_switch.bandwidth_port": { - "title": "Switch Bandwidth for port ", - "units": "kilobits/s", - "type": "area", - "priority": 1, - "family": "ports", - "multiply_range": [ 1, 24 ], - "dimensions": { - "in": { - "oid": "1.3.6.1.2.1.2.2.1.10.", - "algorithm": "incremental", - "multiplier": 8, - "divisor": 1024 - }, - "out": { - "oid": "1.3.6.1.2.1.2.2.1.16.", - "algorithm": "incremental", - "multiplier": -8, - "divisor": 1024 - } - } - } - } - } - ] -} -``` - -The `options` given for each server, are: - - - `timeout`, the time to wait for the SNMP device to respond. The default is 5000 ms. - - `version`, the SNMP version to use. `0` is Version 1, `1` is Version 2c. The default is Version 1 (`0`). - - `transport`, the default is `udp4`. - - `port`, the port of the SNMP device to connect to. The default is `161`. - - `retries`, the number of attempts to make to fetch the data. The default is `1`. - -## Retreiving names from snmp - -You can append a value retrieved from SNMP to the title, by adding `titleoid` to the chart. - -You can set a dimension name to a value retrieved from SNMP, by adding `oidname` to the dimension. - -Both of the above will participate in `multiply_range`. - - -## Testing the configuration - -To test it, you can run: - -```sh -/usr/libexec/netdata/plugins.d/node.d.plugin 1 snmp -``` - -The above will run it on your console and you will be able to see what netdata sees, but also errors. You can get a very detailed output by appending `debug` to the command line. - -If it works, restart netdata to activate the snmp collector and refresh the dashboard (if your SNMP device responds with a delay, you may need to refresh the dashboard in a few seconds). - -## Data collection speed - -Keep in mind that many SNMP switches are routers are very slow. They may not be able to report values per second. If you run `node.d.plugin` in `debug` mode, it will report the time it took for the SNMP device to respond. My switch, for example, needs 7-8 seconds to respond for the traffic on 24 ports (48 OIDs, in/out). - -Also, if you use many SNMP clients on the same SNMP device at the same time, values may be skipped. This is a problem of the SNMP device, not this collector. - -## Finding OIDs - -Use `snmpwalk`, like this: - -```sh -snmpwalk -t 20 -v 1 -O fn -c public 10.11.12.8 -``` - -- `-t 20` is the timeout in seconds -- `-v 1` is the SNMP version -- `-O fn` will display full OIDs in numeric format (you may want to run it also without this option to see human readable output of OIDs) -- `-c public` is the SNMP community -- `10.11.12.8` is the SNMP device - -Keep in mind that `snmpwalk` outputs the OIDs with a dot in front them. You should remove this dot when adding OIDs to the configuration file of this collector. - -## Example: Linksys SRW2024P - -This is what I use for my Linksys SRW2024P. It creates: - -1. A chart for power consumption (it is a PoE switch) -2. Two charts for packets received (total packets received and packets received with errors) -3. One chart for packets output -4. 24 charts, one for each port of the switch. It also appends the port names, as defined at the switch, to the chart titles. - -This switch also reports various other metrics, like snmp, packets per port, etc. Unfortunately it does not report CPU utilization or backplane utilization. - -This switch has a very slow SNMP processors. To respond, it needs about 8 seconds, so I have set the refresh frequency (`update_every`) to 15 seconds. - -```js -{ - "enable_autodetect": false, - "update_every": 5, - "servers": [ - { - "hostname": "10.11.12.8", - "community": "public", - "update_every": 15, - "options": { "timeout": 20000, "version": 1 }, - "charts": { - "snmp_switch.power": { - "title": "Switch Power Supply", - "units": "watts", - "type": "line", - "priority": 10, - "family": "power", - "dimensions": { - "supply": { - "oid": ".1.3.6.1.2.1.105.1.3.1.1.2.1", - "algorithm": "absolute", - "multiplier": 1, - "divisor": 1 - }, - "used": { - "oid": ".1.3.6.1.2.1.105.1.3.1.1.4.1", - "algorithm": "absolute", - "multiplier": 1, - "divisor": 1 - } - } - } - , "snmp_switch.input": { - "title": "Switch Packets Input", - "units": "packets/s", - "type": "area", - "priority": 20, - "family": "IP", - "dimensions": { - "receives": { - "oid": ".1.3.6.1.2.1.4.3.0", - "algorithm": "incremental", - "multiplier": 1, - "divisor": 1 - } - , "discards": { - "oid": ".1.3.6.1.2.1.4.8.0", - "algorithm": "incremental", - "multiplier": 1, - "divisor": 1 - } - } - } - , "snmp_switch.input_errors": { - "title": "Switch Received Packets with Errors", - "units": "packets/s", - "type": "line", - "priority": 30, - "family": "IP", - "dimensions": { - "bad_header": { - "oid": ".1.3.6.1.2.1.4.4.0", - "algorithm": "incremental", - "multiplier": 1, - "divisor": 1 - } - , "bad_address": { - "oid": ".1.3.6.1.2.1.4.5.0", - "algorithm": "incremental", - "multiplier": 1, - "divisor": 1 - } - , "unknown_protocol": { - "oid": ".1.3.6.1.2.1.4.7.0", - "algorithm": "incremental", - "multiplier": 1, - "divisor": 1 - } - } - } - , "snmp_switch.output": { - "title": "Switch Output Packets", - "units": "packets/s", - "type": "line", - "priority": 40, - "family": "IP", - "dimensions": { - "requests": { - "oid": ".1.3.6.1.2.1.4.10.0", - "algorithm": "incremental", - "multiplier": 1, - "divisor": 1 - } - , "discards": { - "oid": ".1.3.6.1.2.1.4.11.0", - "algorithm": "incremental", - "multiplier": -1, - "divisor": 1 - } - , "no_route": { - "oid": ".1.3.6.1.2.1.4.12.0", - "algorithm": "incremental", - "multiplier": -1, - "divisor": 1 - } - } - } - , "snmp_switch.bandwidth_port": { - "title": "Switch Bandwidth for port ", - "titleoid": ".1.3.6.1.2.1.31.1.1.1.18.", - "units": "kilobits/s", - "type": "area", - "priority": 100, - "family": "ports", - "multiply_range": [ 1, 24 ], - "dimensions": { - "in": { - "oid": ".1.3.6.1.2.1.2.2.1.10.", - "algorithm": "incremental", - "multiplier": 8, - "divisor": 1024 - } - , "out": { - "oid": ".1.3.6.1.2.1.2.2.1.16.", - "algorithm": "incremental", - "multiplier": -8, - "divisor": 1024 - } - } - } - } - } - ] -} -``` +# SNMP Data Collector + +Using this collector, netdata can collect data from any SNMP device. + +This collector supports: + +- any number of SNMP devices +- each SNMP device can be used to collect data for any number of charts +- each chart may have any number of dimensions +- each SNMP device may have a different update frequency +- each SNMP device will accept one or more batches to report values (you can set `max_request_size` per SNMP server, to control the size of batches). + +The source code of the plugin is [here](https://github.com/firehol/netdata/blob/master/node.d/snmp.node.js). + +## Configuration + +You will need to create the file `/etc/netdata/node.d/snmp.conf` with data like the following. + +In this example: + + - the SNMP device is `10.11.12.8`. + - the SNMP community is `public`. + - we will update the values every 10 seconds (`update_every: 10` under the server `10.11.12.8`). + - we define 2 charts `snmp_switch.bandwidth_port1` and `snmp_switch.bandwidth_port2`, each having 2 dimensions: `in` and `out`. + +```js +{ + "enable_autodetect": false, + "update_every": 5, + "max_request_size": 100, + "servers": [ + { + "hostname": "10.11.12.8", + "community": "public", + "update_every": 10, + "max_request_size": 50, + "options": { "timeout": 10000 }, + "charts": { + "snmp_switch.bandwidth_port1": { + "title": "Switch Bandwidth for port 1", + "units": "kilobits/s", + "type": "area", + "priority": 1, + "family": "ports", + "dimensions": { + "in": { + "oid": "1.3.6.1.2.1.2.2.1.10.1", + "algorithm": "incremental", + "multiplier": 8, + "divisor": 1024, + "offset": 0 + }, + "out": { + "oid": "1.3.6.1.2.1.2.2.1.16.1", + "algorithm": "incremental", + "multiplier": -8, + "divisor": 1024, + "offset": 0 + } + } + }, + "snmp_switch.bandwidth_port2": { + "title": "Switch Bandwidth for port 2", + "units": "kilobits/s", + "type": "area", + "priority": 1, + "family": "ports", + "dimensions": { + "in": { + "oid": "1.3.6.1.2.1.2.2.1.10.2", + "algorithm": "incremental", + "multiplier": 8, + "divisor": 1024, + "offset": 0 + }, + "out": { + "oid": "1.3.6.1.2.1.2.2.1.16.2", + "algorithm": "incremental", + "multiplier": -8, + "divisor": 1024, + "offset": 0 + } + } + } + } + } + ] +} +``` + +`update_every` is the update frequency for each server, in seconds. + +`max_request_size` limits the maximum number of OIDs that will be requested in a single call. The default is 50. Lower this number of you get `TooBig` errors in netdata error.log. + +`family` sets the name of the submenu of the dashboard each chart will appear under. + +If you need to define many charts using incremental OIDs, you can use something like this: + +This is like the previous, but the option `multiply_range` given, will multiply the current chart from `1` to `24` inclusive, producing 24 charts in total for the 24 ports of the switch `10.11.12.8`. + +Each of the 24 new charts will have its id (1-24) appended at: + +1. its chart unique id, i.e. `snmp_switch.bandwidth_port1` to `snmp_switch.bandwidth_port24` +2. its `title`, i.e. `Switch Bandwidth for port 1` to `Switch Bandwidth for port 24` +3. its `oid` (for all dimensions), i.e. dimension `in` will be `1.3.6.1.2.1.2.2.1.10.1` to `1.3.6.1.2.1.2.2.1.10.24` +3. its priority (which will be incremented for each chart so that the charts will appear on the dashboard in this order) + +```js +{ + "enable_autodetect": false, + "update_every": 10, + "servers": [ + { + "hostname": "10.11.12.8", + "community": "public", + "update_every": 10, + "options": { "timeout": 20000 }, + "charts": { + "snmp_switch.bandwidth_port": { + "title": "Switch Bandwidth for port ", + "units": "kilobits/s", + "type": "area", + "priority": 1, + "family": "ports", + "multiply_range": [ 1, 24 ], + "dimensions": { + "in": { + "oid": "1.3.6.1.2.1.2.2.1.10.", + "algorithm": "incremental", + "multiplier": 8, + "divisor": 1024, + "offset": 0 + }, + "out": { + "oid": "1.3.6.1.2.1.2.2.1.16.", + "algorithm": "incremental", + "multiplier": -8, + "divisor": 1024, + "offset": 0 + } + } + } + } + } + ] +} +``` + +The `options` given for each server, are: + + - `timeout`, the time to wait for the SNMP device to respond. The default is 5000 ms. + - `version`, the SNMP version to use. `0` is Version 1, `1` is Version 2c. The default is Version 1 (`0`). + - `transport`, the default is `udp4`. + - `port`, the port of the SNMP device to connect to. The default is `161`. + - `retries`, the number of attempts to make to fetch the data. The default is `1`. + +## Retreiving names from snmp + +You can append a value retrieved from SNMP to the title, by adding `titleoid` to the chart. + +You can set a dimension name to a value retrieved from SNMP, by adding `oidname` to the dimension. + +Both of the above will participate in `multiply_range`. + + +## Testing the configuration + +To test it, you can run: + +```sh +/usr/libexec/netdata/plugins.d/node.d.plugin 1 snmp +``` + +The above will run it on your console and you will be able to see what netdata sees, but also errors. You can get a very detailed output by appending `debug` to the command line. + +If it works, restart netdata to activate the snmp collector and refresh the dashboard (if your SNMP device responds with a delay, you may need to refresh the dashboard in a few seconds). + +## Data collection speed + +Keep in mind that many SNMP switches are routers are very slow. They may not be able to report values per second. If you run `node.d.plugin` in `debug` mode, it will report the time it took for the SNMP device to respond. My switch, for example, needs 7-8 seconds to respond for the traffic on 24 ports (48 OIDs, in/out). + +Also, if you use many SNMP clients on the same SNMP device at the same time, values may be skipped. This is a problem of the SNMP device, not this collector. + +## Finding OIDs + +Use `snmpwalk`, like this: + +```sh +snmpwalk -t 20 -v 1 -O fn -c public 10.11.12.8 +``` + +- `-t 20` is the timeout in seconds +- `-v 1` is the SNMP version +- `-O fn` will display full OIDs in numeric format (you may want to run it also without this option to see human readable output of OIDs) +- `-c public` is the SNMP community +- `10.11.12.8` is the SNMP device + +Keep in mind that `snmpwalk` outputs the OIDs with a dot in front them. You should remove this dot when adding OIDs to the configuration file of this collector. + +## Example: Linksys SRW2024P + +This is what I use for my Linksys SRW2024P. It creates: + +1. A chart for power consumption (it is a PoE switch) +2. Two charts for packets received (total packets received and packets received with errors) +3. One chart for packets output +4. 24 charts, one for each port of the switch. It also appends the port names, as defined at the switch, to the chart titles. + +This switch also reports various other metrics, like snmp, packets per port, etc. Unfortunately it does not report CPU utilization or backplane utilization. + +This switch has a very slow SNMP processors. To respond, it needs about 8 seconds, so I have set the refresh frequency (`update_every`) to 15 seconds. + +```js +{ + "enable_autodetect": false, + "update_every": 5, + "servers": [ + { + "hostname": "10.11.12.8", + "community": "public", + "update_every": 15, + "options": { "timeout": 20000, "version": 1 }, + "charts": { + "snmp_switch.power": { + "title": "Switch Power Supply", + "units": "watts", + "type": "line", + "priority": 10, + "family": "power", + "dimensions": { + "supply": { + "oid": ".1.3.6.1.2.1.105.1.3.1.1.2.1", + "algorithm": "absolute", + "multiplier": 1, + "divisor": 1, + "offset": 0 + }, + "used": { + "oid": ".1.3.6.1.2.1.105.1.3.1.1.4.1", + "algorithm": "absolute", + "multiplier": 1, + "divisor": 1, + "offset": 0 + } + } + } + , "snmp_switch.input": { + "title": "Switch Packets Input", + "units": "packets/s", + "type": "area", + "priority": 20, + "family": "IP", + "dimensions": { + "receives": { + "oid": ".1.3.6.1.2.1.4.3.0", + "algorithm": "incremental", + "multiplier": 1, + "divisor": 1, + "offset": 0 + } + , "discards": { + "oid": ".1.3.6.1.2.1.4.8.0", + "algorithm": "incremental", + "multiplier": 1, + "divisor": 1, + "offset": 0 + } + } + } + , "snmp_switch.input_errors": { + "title": "Switch Received Packets with Errors", + "units": "packets/s", + "type": "line", + "priority": 30, + "family": "IP", + "dimensions": { + "bad_header": { + "oid": ".1.3.6.1.2.1.4.4.0", + "algorithm": "incremental", + "multiplier": 1, + "divisor": 1, + "offset": 0 + } + , "bad_address": { + "oid": ".1.3.6.1.2.1.4.5.0", + "algorithm": "incremental", + "multiplier": 1, + "divisor": 1, + "offset": 0 + } + , "unknown_protocol": { + "oid": ".1.3.6.1.2.1.4.7.0", + "algorithm": "incremental", + "multiplier": 1, + "divisor": 1, + "offset": 0 + } + } + } + , "snmp_switch.output": { + "title": "Switch Output Packets", + "units": "packets/s", + "type": "line", + "priority": 40, + "family": "IP", + "dimensions": { + "requests": { + "oid": ".1.3.6.1.2.1.4.10.0", + "algorithm": "incremental", + "multiplier": 1, + "divisor": 1, + "offset": 0 + } + , "discards": { + "oid": ".1.3.6.1.2.1.4.11.0", + "algorithm": "incremental", + "multiplier": -1, + "divisor": 1, + "offset": 0 + } + , "no_route": { + "oid": ".1.3.6.1.2.1.4.12.0", + "algorithm": "incremental", + "multiplier": -1, + "divisor": 1, + "offset": 0 + } + } + } + , "snmp_switch.bandwidth_port": { + "title": "Switch Bandwidth for port ", + "titleoid": ".1.3.6.1.2.1.31.1.1.1.18.", + "units": "kilobits/s", + "type": "area", + "priority": 100, + "family": "ports", + "multiply_range": [ 1, 24 ], + "dimensions": { + "in": { + "oid": ".1.3.6.1.2.1.2.2.1.10.", + "algorithm": "incremental", + "multiplier": 8, + "divisor": 1024, + "offset": 0 + } + , "out": { + "oid": ".1.3.6.1.2.1.2.2.1.16.", + "algorithm": "incremental", + "multiplier": -8, + "divisor": 1024, + "offset": 0 + } + } + } + } + } + ] +} +``` diff --git a/conf.d/python.d.conf b/conf.d/python.d.conf index 7e4fa801f..9ed346cdc 100644 --- a/conf.d/python.d.conf +++ b/conf.d/python.d.conf @@ -18,28 +18,53 @@ log_interval: 3600 # ---------------------------------------------------------------------- # Enable / Disable python.d.plugin modules +#default_run: yes # -# The default for all modules is enabled (yes). -# Setting any of these to no will disable it. +# If "default_run" = "yes" the default for all modules is enabled (yes). +# Setting any of these to "no" will disable it. +# +# If "default_run" = "no" the default for all modules is disabled (no). +# Setting any of these to "yes" will enable it. -# apache: yes # apache_cache: yes +# apache: yes +# bind_rndc: yes # cpufreq: yes +# cpuidle: yes # dovecot: yes +# elasticsearch: yes + +# this is just an example example: no + # exim: yes +# fail2ban: yes +# freeradius: yes + +# gunicorn_log has been replaced by web_log +gunicorn_log: no + +# haproxy: yes # hddtemp: yes # ipfs: yes # isc_dhcpd: yes +# mdstat: yes # memcached: yes # mysql: yes # nginx: yes -# nginx_log: yes + +# nginx_log has been replaced by web_log +nginx_log: no + +# ovpn_status_log: yes # phpfpm: yes # postfix: yes +# postgres: yes # redis: yes +# retroshare: yes # sensors: yes +# smartd_log: yes # squid: yes # tomcat: yes -# freeradius: yes -# ovpn_status_log: yes +# varnish: yes +# web_log: yes diff --git a/conf.d/python.d/elasticsearch.conf b/conf.d/python.d/elasticsearch.conf index 1faee8582..f98aaeced 100644 --- a/conf.d/python.d/elasticsearch.conf +++ b/conf.d/python.d/elasticsearch.conf @@ -62,6 +62,13 @@ # cluster_stats: False/True # Calls to cluster stats elasticsearch API. Enabled by default. # # ---------------------------------------------------------------------- +# IMPORTANT Information +# +# Module uses python `requests` package +# +# You need to install it manually. (python-requests or python3-requests depending on the version of python). +# +# # AUTO-DETECTION JOBS # only one of them will run (they have the same name) # diff --git a/conf.d/python.d/fail2ban.conf b/conf.d/python.d/fail2ban.conf index cd805be8d..d9664e353 100644 --- a/conf.d/python.d/fail2ban.conf +++ b/conf.d/python.d/fail2ban.conf @@ -56,22 +56,17 @@ # # Additionally to the above, fail2ban also supports the following: # -# log_path: 'path to fail2ban.log' # Default: '/var/log/fail2ban.log' +# log_path: 'path to fail2ban.log' # Default: '/var/log/fail2ban.log' # conf_path: 'path to jail.local/jail.conf' # Default: '/etc/fail2ban/jail.local' -# exclude: 'jails you want to exclude from autodetection' # Default: '[]' empty list +# conf_dir: 'path to jail.d/' # Default: '' empty +# exclude: 'jails you want to exclude from autodetection' # Default: '[]' empty list #------------------------------------------------------------------------------------------------------------------ -# IMPORTANT Information -# -# fail2ban.log file MUST BE readable by netdata. -# A good idea is to do this by adding the -# # create 0640 root netdata -# to fail2ban conf at logrotate.d -# # ------------------------------------------------------------------------------------------------------------------ # AUTO-DETECTION JOBS # only one of them will run (they have the same name) -#local: -# log_path: '/var/log/fail2ban.log' -# conf_path: '/etc/fail2ban/jail.local' +local: + log_path: '/var/log/fail2ban.log' + conf_path: '/etc/fail2ban/jail.local' +# conf_dir: '/etc/fail2ban/jail.d/' # exclude: 'dropbear apache' diff --git a/conf.d/python.d/gunicorn_log.conf b/conf.d/python.d/gunicorn_log.conf deleted file mode 100644 index 8fea483f0..000000000 --- a/conf.d/python.d/gunicorn_log.conf +++ /dev/null @@ -1,73 +0,0 @@ -# netdata python.d.plugin configuration for nginx gunicorn log -# -# This file is in YaML format. Generally the format is: -# -# name: value -# -# There are 2 sections: -# - global variables -# - one or more JOBS -# -# JOBS allow you to collect values from multiple sources. -# Each source will have its own set of charts. -# -# JOB parameters have to be indented (using spaces only, example below). - -# ---------------------------------------------------------------------- -# Global Variables -# These variables set the defaults for all JOBs, however each JOB -# may define its own, overriding the defaults. - -# update_every sets the default data collection frequency. -# If unset, the python.d.plugin default is used. -# update_every: 1 - -# priority controls the order of charts at the netdata dashboard. -# Lower numbers move the charts towards the top of the page. -# If unset, the default for python.d.plugin is used. -# priority: 60000 - -# retries sets the number of retries to be made in case of failures. -# If unset, the default for python.d.plugin is used. -# Attempts to restore the service are made once every update_every -# and only if the module has collected values in the past. -# retries: 5 - -# ---------------------------------------------------------------------- -# JOBS (data collection sources) -# -# The default JOBS share the same *name*. JOBS with the same name -# are mutually exclusive. Only one of them will be allowed running at -# any time. This allows autodetection to try several alternatives and -# pick the one that works. -# -# Any number of jobs is supported. -# -# All python.d.plugin JOBS (for all its modules) support a set of -# predefined parameters. These are: -# -# job_name: -# name: myname # the JOB's name as it will appear at the -# # dashboard (by default is the job_name) -# # JOBs sharing a name are mutually exclusive -# update_every: 1 # the JOB's data collection frequency -# priority: 60000 # the JOB's order on the dashboard -# retries: 5 # the JOB's number of restoration attempts -# -# Additionally to the above, gunicorn_log also supports the following: -# -# path: 'PATH' # the path to gunicorn's access.log -# - -# ---------------------------------------------------------------------- -# AUTO-DETECTION JOBS -# only one of them will run (they have the same name) - -gunicorn_log: - name: 'local' - path: '/var/log/gunicorn/access.log' - -gunicorn_log2: - name: 'local' - path: '/var/log/gunicorn/gunicorn-access.log' - diff --git a/conf.d/python.d/mongodb.conf b/conf.d/python.d/mongodb.conf new file mode 100644 index 000000000..a19b6570b --- /dev/null +++ b/conf.d/python.d/mongodb.conf @@ -0,0 +1,77 @@ +# netdata python.d.plugin configuration for mongodb +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +# update_every: 1 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# retries sets the number of retries to be made in case of failures. +# If unset, the default for python.d.plugin is used. +# Attempts to restore the service are made once every update_every +# and only if the module has collected values in the past. +# retries: 5 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear at the +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# retries: 5 # the JOB's number of restoration attempts +# +# Additionally to the above, mongodb also supports the following: +# +# host: 'IP or HOSTNAME' # type the host to connect to +# port: PORT # type the port to connect to +# +# in all cases, the following can also be set: +# +# user: 'username' # the mongodb username to use +# pass: 'password' # the mongodb password to use +# + +# ---------------------------------------------------------------------- +# to connect to the mongodb on localhost, without a password: +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) + +local: + name : 'local' + host : '127.0.0.1' + port : 27017 diff --git a/conf.d/python.d/nginx_log.conf b/conf.d/python.d/nginx_log.conf deleted file mode 100644 index 6a53c5204..000000000 --- a/conf.d/python.d/nginx_log.conf +++ /dev/null @@ -1,72 +0,0 @@ -# netdata python.d.plugin configuration for nginx log -# -# This file is in YaML format. Generally the format is: -# -# name: value -# -# There are 2 sections: -# - global variables -# - one or more JOBS -# -# JOBS allow you to collect values from multiple sources. -# Each source will have its own set of charts. -# -# JOB parameters have to be indented (using spaces only, example below). - -# ---------------------------------------------------------------------- -# Global Variables -# These variables set the defaults for all JOBs, however each JOB -# may define its own, overriding the defaults. - -# update_every sets the default data collection frequency. -# If unset, the python.d.plugin default is used. -# update_every: 1 - -# priority controls the order of charts at the netdata dashboard. -# Lower numbers move the charts towards the top of the page. -# If unset, the default for python.d.plugin is used. -# priority: 60000 - -# retries sets the number of retries to be made in case of failures. -# If unset, the default for python.d.plugin is used. -# Attempts to restore the service are made once every update_every -# and only if the module has collected values in the past. -# retries: 5 - -# ---------------------------------------------------------------------- -# JOBS (data collection sources) -# -# The default JOBS share the same *name*. JOBS with the same name -# are mutually exclusive. Only one of them will be allowed running at -# any time. This allows autodetection to try several alternatives and -# pick the one that works. -# -# Any number of jobs is supported. -# -# All python.d.plugin JOBS (for all its modules) support a set of -# predefined parameters. These are: -# -# job_name: -# name: myname # the JOB's name as it will appear at the -# # dashboard (by default is the job_name) -# # JOBs sharing a name are mutually exclusive -# update_every: 1 # the JOB's data collection frequency -# priority: 60000 # the JOB's order on the dashboard -# retries: 5 # the JOB's number of restoration attempts -# -# Additionally to the above, nginx_log also supports the following: -# -# path: 'PATH' # the path to nginx's access.log -# - -# ---------------------------------------------------------------------- -# AUTO-DETECTION JOBS -# only one of them will run (they have the same name) - -nginx_log: - name: 'local' - path: '/var/log/nginx/access.log' - -nginx_log2: - name: 'local' - path: '/var/log/nginx/nginx-access.log' diff --git a/conf.d/python.d/nsd.conf b/conf.d/python.d/nsd.conf new file mode 100644 index 000000000..7566fe85e --- /dev/null +++ b/conf.d/python.d/nsd.conf @@ -0,0 +1,86 @@ +# netdata python.d.plugin configuration for nsd +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +# nsd-control is slow, so once every 30 seconds +# update_every: 30 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# retries sets the number of retries to be made in case of failures. +# If unset, the default for python.d.plugin is used. +# Attempts to restore the service are made once every update_every +# and only if the module has collected values in the past. +# retries: 5 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear at the +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# retries: 5 # the JOB's number of restoration attempts +# +# Additionally to the above, nsd also supports the following: +# +# command: 'nsd-control stats_noreset' # the command to run +# + +# ---------------------------------------------------------------------- +# IMPORTANT Information +# +# Netdata must have permissions to run `nsd-control stats_noreset` command +# +# - Example-1 (use "sudo") +# 1. sudoers (e.g. visudo -f /etc/sudoers.d/netdata) +# Defaults:netdata !requiretty +# netdata ALL=(ALL) NOPASSWD: /usr/sbin/nsd-control stats_noreset +# 2. etc/netdata/python.d/nsd.conf +# local: +# update_every: 30 +# command: 'sudo /usr/sbin/nsd-control stats_noreset' +# +# - Example-2 (add "netdata" user to "nsd" group) +# usermod -aG nsd netdata +# + +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS + +local: + update_every: 30 + command: 'nsd-control stats_noreset' diff --git a/conf.d/python.d/smartd_log.conf b/conf.d/python.d/smartd_log.conf new file mode 100644 index 000000000..e16454dfb --- /dev/null +++ b/conf.d/python.d/smartd_log.conf @@ -0,0 +1,85 @@ +# netdata python.d.plugin configuration for smartd log +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +# update_every: 1 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# retries sets the number of retries to be made in case of failures. +# If unset, the default for python.d.plugin is used. +# Attempts to restore the service are made once every update_every +# and only if the module has collected values in the past. +# retries: 5 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear at the +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# retries: 5 # the JOB's number of restoration attempts +# +# Additionally to the above, smartd_log also supports the following: +# +# log_path: '/path/to/smartdlogs' # path to smartd log files. Default is /var/log/smartd +# raw_values: no # raw or normalized values on charts. Default is normalized. +# smart_attributes: '1 2 3 4 44' # add additional smart attributes charts. Default are ['1', '4', '5', '7', '9', '12', '193', '194', '197', '198', '200']. +# +# ---------------------------------------------------------------------- +# Additional information +# Plugin reads smartd log files (-A option). +# You need to add (man smartd) to /etc/default/smartmontools '-i 600 -A /var/log/smartd/' to pass additional options to smartd on startup +# Then restart smartd service and check /path/log/smartdlogs +# ls /var/log/smartd/ +# CDC_WD10EZEX_00BN5A0-WD_WCC3F7FLVZS9.ata.csv WDC_WD10EZEX_00BN5A0-WD_WCC3F7FLVZS9.ata.csv ZDC_WD10EZEX_00BN5A0-WD_WCC3F7FLVZS9.ata.csv +# +# Smartd APPEND logs at every run. Its NOT RECOMMENDED to set '-i' option below 60 sec. +# STRONGLY RECOMMENDED to create smartd conf file for logrotate +# +# RAW vs NORMALIZED values +# "Normalized value", commonly referred to as just "value". This is a most universal measurement, on the scale from 0 (bad) to some maximum (good) value. +# Maximum values are typically 100, 200 or 253. Rule of thumb is: high values are good, low values are bad. +# +# "Raw value" - the value of the attribute as it is tracked by the device, before any normalization takes place. +# Some raw numbers provide valuable insight when properly interpreted. These cases will be discussed later on. +# Raw values are typically listed in hexadecimal numbers. The raw value has different structure for different vendors and is often not meaningful as a decimal number. +# +# +# JOB configuration +# +log_path: '/var/log/smartd' diff --git a/conf.d/python.d/varnish.conf b/conf.d/python.d/varnish.conf index 56dc6334d..c25f3010f 100644 --- a/conf.d/python.d/varnish.conf +++ b/conf.d/python.d/varnish.conf @@ -55,11 +55,3 @@ # retries: 5 # the JOB's number of restoration attempts # # -# -# The only you need is to add netdata to 'varnish' group -# -# Check it from cmd -# id netdata -# -# uid=999(netdata) gid=999(netdata) группы=999(netdata),118(varnish) -# diff --git a/conf.d/python.d/web_log.conf b/conf.d/python.d/web_log.conf new file mode 100644 index 000000000..06656285f --- /dev/null +++ b/conf.d/python.d/web_log.conf @@ -0,0 +1,147 @@ +# netdata python.d.plugin configuration for web log +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +# update_every: 1 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# retries sets the number of retries to be made in case of failures. +# If unset, the default for python.d.plugin is used. +# Attempts to restore the service are made once every update_every +# and only if the module has collected values in the past. +# retries: 5 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. + +# ---------------------------------------------------------------------- +# PLUGIN CONFIGURATION +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear at the +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# retries: 5 # the JOB's number of restoration attempts +# +# Additionally to the above, web_log also supports the following: +# +# path: 'PATH' # the path to web server log file +# detailed_response_codes: yes/no # Default: yes. Additional chart where response codes are not grouped +# detailed_response_aggregate: yes/no # Default: yes. Not aggregated detailed response codes charts +# all_time : yes/no # Default: yes. All time unique client IPs chart (50000 addresses ~ 400KB) +# categories: # requests per url chart configuration +# cacti: 'cacti.*' # name(dimension): REGEX to match +# observium: 'observium.*' # name(dimension): REGEX to match +# stub_status: 'stub_status' # name(dimension): REGEX to match +# custom_log_format: # define a custom log format +# pattern: '(?P
[\da-f.:]+) -.*?"(?P[A-Z]+) (?P.*?)" (?P[1-9]\d{2}) (?P\d+) (?P\d+) (?P\d\.\d+) ' +# time_multiplier: 1000000 # type - convert time to microseconds + +# ---------------------------------------------------------------------- +# WEB SERVER CONFIGURATION +# +# Make sure the web server log directory and the web server log files +# can be read by user 'netdata'. +# +# To enable the timings chart and the requests size dimension, the +# web server needs to log them. This is how to add them: +# +# nginx: +# log_format netdata '$remote_addr - $remote_user [$time_local] ' +# '"$request" $status $body_bytes_sent ' +# '$request_length $request_time ' +# '"$http_referer" "$http_user_agent"'; +# access_log /var/log/nginx/access.log netdata; +# +# apache (you need mod_logio enabled): +# LogFormat "%h %l %u %t \"%r\" %>s %O %I %D \"%{Referer}i\" \"%{User-Agent}i\"" vhost_netdata +# LogFormat "%h %l %u %t \"%r\" %>s %O %I %D \"%{Referer}i\" \"%{User-Agent}i\"" netdata +# CustomLog "/var/log/apache2/access.log" netdata + +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# only one of them per web server will run (when they have the same name) + + +# ------------------------------------------- +# nginx log on various distros + +# debian, arch +nginx_log: + name: 'nginx' + path: '/var/log/nginx/access.log' + +# gentoo +nginx_log2: + name: 'nginx' + path: '/var/log/nginx/localhost.access_log' + + +# ------------------------------------------- +# apache log on various distros + +# debian +apache_log: + name: 'apache' + path: '/var/log/apache2/access.log' + +# gentoo +apache_log2: + name: 'apache' + path: '/var/log/apache2/access_log' + +# arch +apache_log3: + name: 'apache' + path: '/var/log/httpd/access_log' + +# debian +apache_vhosts_log: + name: 'apache_vhosts' + path: '/var/log/apache2/other_vhosts_access.log' + + +# ------------------------------------------- +# gunicorn log on various distros + +gunicorn_log: + name: 'gunicorn' + path: '/var/log/gunicorn/access.log' + +gunicorn_log2: + name: 'gunicorn' + path: '/var/log/gunicorn/gunicorn-access.log' diff --git a/conf.d/stream.conf b/conf.d/stream.conf new file mode 100644 index 000000000..0ebdccb8a --- /dev/null +++ b/conf.d/stream.conf @@ -0,0 +1,143 @@ +# netdata configuration for aggregating data from remote hosts +# +# API keys authorize a pair of sending-receiving netdata servers. +# Once their communication is authorized, they can exchange metrics for any +# number of hosts. +# +# You can generate API keys, with the linux command: uuidgen +# +# ----------------------------------------------------------------------------- +# 1. ON SLAVE NETDATA - THE ONE THAT WILL BE SENDING METRICS + +[stream] + # Enable this on slaves, to have them send metrics. + enabled = no + + # The destination to send metrics to. + # A space separated list of: + # [PROTOCOL:]HOST[%INTERFACE][:PORT] + # The first available will get the metrics. + # PROTOCOL = tcp or udp (only tcp is supported by masters) + # HOST = an IPv4, IPv6 IP, or a hostname. + # IPv6 IPs should be given with brackets [ip:address] + # INTERFACE = the network interface to use + # PORT = the port number or service name (/etc/services) + # This communication is not HTTP (cannot be proxied by web proxies). + destination = + + # The API_KEY to use (as the sender) + api key = + + # The timeout to connect and send metrics + timeout seconds = 60 + + # If the destination line above does specify a port, use this + default port = 19999 + + # The buffer to use for sending metrics. + # 1MB by default is good for 2-3 seconds of data, so increase this + # if you expect latencies. + buffer size bytes = 1048576 + + # If the connection fails, or it disconnects, + # retry after that many seconds. + reconnect delay seconds = 5 + + # Attempt to sync the clock the of the master with the clock of the + # slave for that many iterations, when starting. + initial clock resync iterations = 60 + + +# ----------------------------------------------------------------------------- +# 2. ON MASTER NETDATA - THE ONE THAT WILL BE RECEIVING METRICS +# +# You can have one API key per slave, or the same API key for all slaves. +# +# All options below are used in this order: +# +# a) MACHINE_GUID (settings for each machine) +# b) API_KEY (settings for the API key) +# c) this netdata defaults (as in netdata.conf) +# +# You can combine the above (the more specific setting will be used). + +# API key authentication +# If the key is not listed here, it will not be able to connect. + +[API_KEY] + # Default settings for the API key + + # You can disable the API key, by setting this to: no + # The default (for unknown API keys) is also: no + enabled = no + + # The default history in entries, for all hosts using this API key. + # You can also set it per host below. + # If you don't set it here, the history size of the central netdata + # will be used + default history = 3600 + + # The default memory mode to be used for all hosts using this API key. + # You can also set it per host below. + # If you don't set it here, the memory mode of netdata.conf will be used. + # Valid modes: + # save save on exit, load on start + # map like swap (continuously syncing to disks) + # ram keep it in RAM, don't touch the disk + # none no database (passing through this netdata) + default memory mode = ram + + # Shall we enable health monitoring for the hosts using this API key? + # 3 values: + # yes enable alarms + # no do not enable alarms + # auto enable alarms, only when the sending netdata is connected + # You can also set it per host, below. + # The default is the same as to netdata.conf + health enabled by default = auto + + # postpone alarms for a short period after the sender is connected + default postpone alarms on connect seconds = 60 + + # need to route metrics differently? set these. + # the defaults are the ones at the [stream] section + #default proxy enabled = yes | no + #default proxy destination = IP:PORT IP:PORT ... + #default proxy api key = API_KEY + + +# ----------------------------------------------------------------------------- +# 3. ON MASTER NETDATA - THE ONE THAT WILL BE RECEIVING METRICS +# +# THIS IS OPTIONAL - YOU DON'T NEED IT BY DEFAULT +# It only exists to give you finer control of the master settings for each +# slave host, when the same API key is used by many netdata slaves / proxies. +# +# Each netdata has a unique GUID - generated the first time netdata starts. +# You can find it at /var/lib/netdata/registry/netdata.public.unique.id +# The host sending data will have one. If the host is not ephemeral, +# you can give settings for each specific host here. + +[MACHINE_GUID] + # enable this host: yes | no + # When disabled, the master will not receive metrics for this host. + # THIS IS NOT A SECURITY MECHANISM - AN ATTACKER CAN SET ANY OTHER GUID. + # Use only the API key for security. + enabled = no + + # The number of entries in the database + history = 3600 + + # The memory mode of the database: save | map | ram | none + memory mode = save + + # Health / alarms control: yes | no | auto + health enabled = yes + + # postpone alarms when the sender connects + postpone alarms on connect seconds = 60 + + # need to route metrics differently? + #proxy enabled = yes | no + #proxy destination = IP:PORT IP:PORT ... + #proxy api key = API_KEY diff --git a/config.guess b/config.guess index a2eeec38e..d622a44e5 100755 --- a/config.guess +++ b/config.guess @@ -1,12 +1,14 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2016 Free Software Foundation, Inc. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +# 2011, 2012 Free Software Foundation, Inc. -timestamp='2016-04-02' +timestamp='2012-02-10' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or +# the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -20,17 +22,19 @@ timestamp='2016-04-02' # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. # -# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess -# -# Please send patches to . - +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` @@ -50,7 +54,9 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2016 Free Software Foundation, Inc. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 +Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -132,27 +138,6 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown -case "${UNAME_SYSTEM}" in -Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu - - eval $set_cc_for_build - cat <<-EOF > $dummy.c - #include - #if defined(__UCLIBC__) - LIBC=uclibc - #elif defined(__dietlibc__) - LIBC=dietlibc - #else - LIBC=gnu - #endif - EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` - ;; -esac - # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in @@ -168,27 +153,20 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ - /sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || \ - echo unknown)` + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; - earmv*) - arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` - endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` - machine=${arch}${endian}-unknown - ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in - arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax) + arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ @@ -204,13 +182,6 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in os=netbsd ;; esac - # Determine ABI tags. - case "${UNAME_MACHINE_ARCH}" in - earm*) - expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` - ;; - esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need @@ -221,26 +192,18 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}${abi}" - exit ;; - *:Bitrig:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + echo "${machine}-${os}${release}" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; - *:LibertyBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} - exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; @@ -253,9 +216,6 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; - *:Sortix:*:*) - echo ${UNAME_MACHINE}-unknown-sortix - exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) @@ -272,42 +232,42 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") - UNAME_MACHINE=alpha ;; + UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") - UNAME_MACHINE=alpha ;; + UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") - UNAME_MACHINE=alpha ;; + UNAME_MACHINE="alpha" ;; "EV5 (21164)") - UNAME_MACHINE=alphaev5 ;; + UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") - UNAME_MACHINE=alphaev56 ;; + UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") - UNAME_MACHINE=alphapca56 ;; + UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") - UNAME_MACHINE=alphapca57 ;; + UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") - UNAME_MACHINE=alphaev6 ;; + UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") - UNAME_MACHINE=alphaev67 ;; + UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") - UNAME_MACHINE=alphaev68 ;; + UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") - UNAME_MACHINE=alphaev68 ;; + UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") - UNAME_MACHINE=alphaev68 ;; + UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE=alphaev69 ;; + UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") - UNAME_MACHINE=alphaev7 ;; + UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") - UNAME_MACHINE=alphaev79 ;; + UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 @@ -342,7 +302,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; - arm*:riscos:*:*|arm*:RISCOS:*:*) + arm:riscos:*:*|arm:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) @@ -380,16 +340,16 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build - SUN_ARCH=i386 + SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - SUN_ARCH=x86_64 + SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` @@ -414,7 +374,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} @@ -600,9 +560,8 @@ EOF else IBM_ARCH=powerpc fi - if [ -x /usr/bin/lslpp ] ; then - IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | - awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi @@ -639,13 +598,13 @@ EOF sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in - 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 - 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in - 32) HP_ARCH=hppa2.0n ;; - 64) HP_ARCH=hppa2.0w ;; - '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi @@ -684,11 +643,11 @@ EOF exit (0); } EOF - (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = hppa2.0w ] + if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build @@ -701,12 +660,12 @@ EOF # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH=hppa2.0w + HP_ARCH="hppa2.0w" else - HP_ARCH=hppa64 + HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} @@ -811,14 +770,14 @@ EOF echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` - FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) @@ -842,13 +801,10 @@ EOF i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; - *:MINGW64*:*) - echo ${UNAME_MACHINE}-pc-mingw64 - exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; - *:MSYS*:*) + i*:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) @@ -896,21 +852,21 @@ EOF exit ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in @@ -923,60 +879,59 @@ EOF EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC=gnulibc1 ; fi - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + echo ${UNAME_MACHINE}-unknown-linux-gnueabi else - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + echo ${UNAME_MACHINE}-unknown-linux-gnueabihf fi fi exit ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} + echo ${UNAME_MACHINE}-axis-linux-gnu exit ;; crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; - e2k:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-axis-linux-gnu exit ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" exit ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - k1om:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build @@ -995,74 +950,54 @@ EOF #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; - openrisc*:Linux:*:*) - echo or1k-unknown-linux-${LIBC} - exit ;; - or32:Linux:*:* | or1k*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + or32:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; padre:Linux:*:*) - echo sparc-unknown-linux-${LIBC} + echo sparc-unknown-linux-gnu exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-${LIBC} + echo hppa64-unknown-linux-gnu exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; - PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; - *) echo hppa-unknown-linux-${LIBC} ;; + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; esac exit ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-${LIBC} + echo powerpc64-unknown-linux-gnu exit ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-${LIBC} - exit ;; - ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-${LIBC} - exit ;; - ppcle:Linux:*:*) - echo powerpcle-unknown-linux-${LIBC} + echo powerpc-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-${LIBC} + echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) - eval $set_cc_for_build - X86_64_ABI= - # If there is a compiler, see if it is configured for 32-bit objects. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_X32 >/dev/null - then - X86_64_ABI=x32 - fi - fi - echo ${UNAME_MACHINE}-pc-linux-${LIBC}${X86_64_ABI} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. @@ -1138,7 +1073,7 @@ EOF # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configure will decide that + # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; @@ -1266,9 +1201,6 @@ EOF BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; - x86_64:Haiku:*:*) - echo x86_64-unknown-haiku - exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; @@ -1287,9 +1219,6 @@ EOF SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; - SX-ACE:SUPER-UX:*:*) - echo sxace-nec-superux${UNAME_RELEASE} - exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; @@ -1298,36 +1227,24 @@ EOF exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval $set_cc_for_build - if test "$UNAME_PROCESSOR" = unknown ; then - UNAME_PROCESSOR=powerpc - fi - if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi - fi - elif test "$UNAME_PROCESSOR" = i386 ; then - # Avoid executing cc on OS X 10.9, as it ships with a stub - # that puts up a graphical alert prompting to install - # developer tools. Any system running Mac OS X 10.7 or - # later (Darwin 11 and later) is required to have a 64-bit - # processor. This is not true of the ARM version of Darwin - # that Apple uses in portable devices. - UNAME_PROCESSOR=x86_64 - fi + case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = x86; then + if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi @@ -1339,7 +1256,7 @@ EOF NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; - NSE-*:NONSTOP_KERNEL:*:*) + NSE-?:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) @@ -1358,7 +1275,7 @@ EOF # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = 386; then + if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" @@ -1400,7 +1317,7 @@ EOF echo i386-pc-xenix exit ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos @@ -1411,11 +1328,159 @@ EOF x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; - amd64:Isilon\ OneFS:*:*) - echo x86_64-unknown-onefs - exit ;; esac +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + cat >&2 < header file. */ #undef HAVE_ARPA_NAMESER_H +/* libcap usability */ +#undef HAVE_CAPABILITY + /* Define to 1 if the system has the type `clockid_t'. */ #undef HAVE_CLOCKID_T @@ -34,6 +37,9 @@ don't. */ #undef HAVE_DECL_STRERROR_R +/* ipmimonitoring usability */ +#undef HAVE_FREEIPMI + /* Define to 1 if the system has the `format' function attribute */ #undef HAVE_FUNC_ATTRIBUTE_FORMAT @@ -56,6 +62,19 @@ /* Define to 1 if you have the header file. */ #undef HAVE_JEMALLOC_JEMALLOC_H +/* libmnl usability */ +#undef HAVE_LIBMNL + +/* libnetfilter_acct usability */ +#undef HAVE_LIBNETFILTER_ACCT + +/* Define to 1 if you have the header + file. */ +#undef HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H + +/* Define to 1 if -flto works. */ +#undef HAVE_LTO + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H @@ -65,6 +84,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H +/* Define to 1 if you have the `nice' function. */ +#undef HAVE_NICE + /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD @@ -74,6 +96,15 @@ /* Define to 1 if you have the header file. */ #undef HAVE_RESOLV_H +/* Define to 1 if you have the `sched_get_priority_max' function. */ +#undef HAVE_SCHED_GET_PRIORITY_MAX + +/* Define to 1 if you have the `sched_get_priority_min' function. */ +#undef HAVE_SCHED_GET_PRIORITY_MIN + +/* Define to 1 if you have the `sched_setscheduler' function. */ +#undef HAVE_SCHED_SETSCHEDULER + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H @@ -92,6 +123,9 @@ /* Define to 1 if the system has the type `struct timespec'. */ #undef HAVE_STRUCT_TIMESPEC +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PRCTL_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H @@ -101,7 +135,7 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H -/* nfacct plugin settings */ +/* nfacct plugin usability */ #undef INTERNAL_PLUGIN_NFACCT /* Define to 1 if `major', `minor', and `makedev' are declared in . @@ -115,10 +149,10 @@ /* use this user to drop privileged */ #undef NETDATA_USER -/* uuid settings */ +/* uuid usability */ #undef NETDATA_WITH_UUID -/* zlib settings */ +/* zlib usability */ #undef NETDATA_WITH_ZLIB /* Name of package */ @@ -152,7 +186,7 @@ /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS -/* math settings */ +/* math usability */ #undef STORAGE_WITH_MATH /* Define to 1 if strerror_r returns char *. */ @@ -198,6 +232,11 @@ #define below would cause a syntax error. */ #undef _UINT32_T +/* Define for Solaris 2.5.1 so the uint64_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT64_T + /* Define for Solaris 2.5.1 so the uint8_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ @@ -215,6 +254,22 @@ #undef inline #endif +/* Define to the type of a signed integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef int16_t + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef int32_t + +/* Define to the type of a signed integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef int64_t + +/* Define to the type of a signed integer type of width exactly 8 bits if such + a type exists and the standard includes do not define it. */ +#undef int8_t + /* gcc branch optimization */ #undef likely @@ -229,6 +284,10 @@ such a type exists and the standard includes do not define it. */ #undef uint32_t +/* Define to the type of an unsigned integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef uint64_t + /* Define to the type of an unsigned integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef uint8_t diff --git a/config.sub b/config.sub index b01fff0b6..c894da455 100755 --- a/config.sub +++ b/config.sub @@ -1,18 +1,24 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2016 Free Software Foundation, Inc. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +# 2011, 2012 Free Software Foundation, Inc. -timestamp='2016-03-30' +timestamp='2012-02-10' -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . @@ -20,12 +26,11 @@ timestamp='2016-03-30' # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). +# the same distribution terms that you use for the rest of that program. -# Please send patches to . +# Please send patches to . Submit a context +# diff and a properly formatted GNU ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. @@ -33,7 +38,7 @@ timestamp='2016-03-30' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -53,7 +58,8 @@ timestamp='2016-03-30' me=`echo "$0" | sed -e 's,.*/,,'` usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS Canonicalize a configuration name. @@ -67,7 +73,9 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright 1992-2016 Free Software Foundation, Inc. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 +Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -115,8 +123,8 @@ esac maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os @@ -148,7 +156,7 @@ case $os in -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray | -microblaze*) + -apple | -axis | -knuth | -cray | -microblaze) os= basic_machine=$1 ;; @@ -217,12 +225,6 @@ case $os in -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; - -lynx*178) - os=-lynxos178 - ;; - -lynx*5) - os=-lynxos5 - ;; -lynx*) os=-lynxos ;; @@ -251,25 +253,21 @@ case $basic_machine in | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ - | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ - | avr | avr32 \ - | ba \ - | be32 | be64 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | be32 | be64 \ | bfin \ - | c4x | c8051 | clipper \ - | d10v | d30v | dlx | dsp16xx | dvp \ - | e2k | epiphany \ - | fido | fr30 | frv | ft32 \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | epiphany \ + | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ - | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | maxq | mb | microblaze | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ @@ -283,29 +281,26 @@ case $basic_machine in | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ - | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ - | nios | nios2 | nios2eb | nios2el \ + | nios | nios2 \ | ns16k | ns32k \ - | open8 | or1k | or1knd | or32 \ + | open8 \ + | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ - | riscv32 | riscv64 \ | rl78 | rx \ | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ @@ -313,7 +308,6 @@ case $basic_machine in | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) @@ -328,10 +322,7 @@ case $basic_machine in c6x) basic_machine=tic6x-unknown ;; - leon|leon[3-9]) - basic_machine=sparc-$basic_machine - ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) basic_machine=$basic_machine-unknown os=-none ;; @@ -373,29 +364,26 @@ case $basic_machine in | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ - | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | c8051-* | clipper-* | craynv-* | cydra-* \ + | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ - | e2k-* | elxsi-* \ + | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ - | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ - | microblaze-* | microblazeel-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ @@ -409,33 +397,28 @@ case $basic_machine in | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | nios-* | nios2-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ - | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ - | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ @@ -443,7 +426,6 @@ case $basic_machine in | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ - | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ @@ -520,9 +502,6 @@ case $basic_machine in basic_machine=i386-pc os=-aros ;; - asmjs) - basic_machine=asmjs-unknown - ;; aux) basic_machine=m68k-apple os=-aux @@ -784,9 +763,6 @@ case $basic_machine in basic_machine=m68k-isi os=-sysv ;; - leon-*|leon[3-9]-*) - basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` - ;; m68knommu) basic_machine=m68k-unknown os=-linux @@ -806,15 +782,11 @@ case $basic_machine in basic_machine=ns32k-utek os=-sysv ;; - microblaze*) + microblaze) basic_machine=microblaze-xilinx ;; - mingw64) - basic_machine=x86_64-pc - os=-mingw64 - ;; mingw32) - basic_machine=i686-pc + basic_machine=i386-pc os=-mingw32 ;; mingw32ce) @@ -828,24 +800,6 @@ case $basic_machine in basic_machine=m68k-atari os=-mint ;; - mipsEE* | ee | ps2) - basic_machine=mips64r5900el-scei - case $os in - -linux*) - ;; - *) - os=-elf - ;; - esac - ;; - iop) - basic_machine=mipsel-scei - os=-irx - ;; - dvp) - basic_machine=dvp-scei - os=-elf - ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; @@ -860,10 +814,6 @@ case $basic_machine in basic_machine=powerpc-unknown os=-morphos ;; - moxiebox) - basic_machine=moxie-unknown - os=-moxiebox - ;; msdos) basic_machine=i386-pc os=-msdos @@ -872,7 +822,7 @@ case $basic_machine in basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) - basic_machine=i686-pc + basic_machine=i386-pc os=-msys ;; mvs) @@ -1063,11 +1013,7 @@ case $basic_machine in basic_machine=i586-unknown os=-pw32 ;; - rdos | rdos64) - basic_machine=x86_64-pc - os=-rdos - ;; - rdos32) + rdos) basic_machine=i386-pc os=-rdos ;; @@ -1394,30 +1340,29 @@ case $os in -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ - | -sym* | -kopensolaris* | -plan9* \ + | -sym* | -kopensolaris* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* | -cloudabi* | -sortix* \ + | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ + | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ - | -linux-newlib* | -linux-musl* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -mingw32* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -irx* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ - | -onefs* | -tirtos*) + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1541,6 +1486,9 @@ case $os in -aros*) os=-aros ;; + -kaos*) + os=-kaos + ;; -zvmoe) os=-zvmoe ;; @@ -1549,8 +1497,6 @@ case $os in ;; -nacl*) ;; - -ios) - ;; -none) ;; *) @@ -1591,12 +1537,6 @@ case $basic_machine in c4x-* | tic4x-*) os=-coff ;; - c8051-*) - os=-elf - ;; - hexagon-*) - os=-elf - ;; tic54x-*) os=-coff ;; diff --git a/configs.signatures b/configs.signatures index fbae919c4..713a1d323 100644 --- a/configs.signatures +++ b/configs.signatures @@ -1,6 +1,7 @@ declare -A configs_signatures=( ['0056936ce99788ed9ae1c611c87aa6d8']='apps_groups.conf' ['0102351817595a85d01ebd54a5f2f36b']='python.d/ovpn_status_log.conf' + ['01302e01162d465614276de43fad7546']='python.d.conf' ['02fa10fa85ab88e9723998de48d1aca0']='health.d/disks.conf' ['036dc300bd7b0e0ef229b9822686d63e']='python.d/isc_dhcpd.conf' ['0388b873d0d7e47c19005b7241db77d8']='python.d/tomcat.conf' @@ -9,6 +10,8 @@ declare -A configs_signatures=( ['043f0a35dde85837fabeb85b990a41c1']='health.d/swap.conf' ['0529b679d3c0e7e6332753c7f6484731']='health.d/net.conf' ['057d12aaff0467e64529e839a258806b']='health.d/entropy.conf' + ['059d98d0c562e1c81653d1e64673deab']='python.d/web_log.conf' + ['05a8f39f134850c1e8d6267dbe706273']='health.d/web_log.conf' ['061c45b0e34170d357e47883166ecf40']='python.d/nginx.conf' ['074df527cc70b5f38c0714f08f20e57c']='health.d/apache.conf' ['08042325ab27256b938575deafee8ecf']='python.d/nginx.conf' @@ -19,9 +22,11 @@ declare -A configs_signatures=( ['091572888425bc3b8b559c3c53367ec7']='apps_groups.conf' ['09264cec953ae1c4c2985e6446abb386']='health.d/mysql.conf' ['093540fdc2a228e976ce5d48a3adf9fc']='health.d/disks.conf' + ['0ad10fa896346202aee99384b0ec968d']='health.d/cpu.conf' ['0c5e0fa364d7bdf7c16e8459a0544572']='health.d/netfilter.conf' ['0cd4e1fb57497e4d4c2451a9e58f724d']='python.d/redis.conf' ['0d29fe9919a2975107db1f2583344e7a']='health.d/mdstat.conf' + ['0dd38dcd2473ddb9f8b1b41147432d10']='health_alarm_notify.conf' ['0e59bc11d0a869ea0247c04c08c8d72e']='python.d/ipfs.conf' ['0ef8af1f358741afa7fd5d0ffabefaac']='charts.d/mysql.conf' ['10c3b525850a1cb9de760a8ee96fbc6e']='charts.d/opensips.conf' @@ -32,11 +37,13 @@ declare -A configs_signatures=( ['15d8401b56a74120f9f832873ec9c578']='health.d/postgres.conf' ['15e32114994b92be7853b88091e7c6fb']='python.d/exim.conf' ['174c21a6ce5de97bda83d502aa47a9f8']='health.d/apache.conf' + ['17555c7418c801ceb6c93adbe485d6f9']='apps_groups.conf' ['178281aa2241d4a3e6b798bb9c4ae577']='python.d/haproxy.conf' ['18710ef6523cef8630d644ab270bfe02']='health.d/varnish.conf' ['18ee1c6197a4381b1c1631ef6129824f']='apps_groups.conf' ['1972e48345e6c3f0d65f94a03317622b']='health_alarm_notify.conf' ['1c12b678ab65f271a96da1bbd0a1ab1c']='health.d/softnet.conf' + ['1c3168c95b53e999df3d45162b3f50b8']='health.d/fping.conf' ['1ea8e8ef1fa8a3a0fcdfba236f4cb195']='python.d/mysql.conf' ['1ef0fd38e7969c023bc3fa6d89eaf6d6']='python.d/mdstat.conf' ['1f5545b3ff52b3eb75ee05401f67a9bc']='fping.conf' @@ -56,6 +63,7 @@ declare -A configs_signatures=( ['2827de41cf34a91b7a8e4d8724f59668']='health.d/net.conf' ['28df44a90e8ea4c6156314c03e88bf44']='health.d/softnet.conf' ['297160ae7ee01a547ed14f857b4f2c8d']='health.d/memcached.conf' + ['298504f331c55dff4055321ff3a7b5cc']='health.d/web_log.conf' ['29f97e10b92333790fbe0d2a3617b736']='health_alarm_notify.conf' ['2a0794fd43eadf30a51805bc9ba2c64d']='python.d/hddtemp.conf' ['2ad55a5d1e885cf142849a78d4b00401']='health.d/net.conf' @@ -67,14 +75,19 @@ declare -A configs_signatures=( ['312b4b8e2805e19cf9be554b319567d6']='health.d/softnet.conf' ['318bb45755726a25120bb33413d4b582']='health.d/net.conf' ['325617412a628e3bc776e3fbb777a2a6']='health.d/redis.conf' + ['326e1477131e0f73304711135f70a2a5']='health.d/memcached.conf' ['32fde0057c790964f2c743cb3c9aad29']='health.d/nginx.conf' ['33b135e28aeaef2b8224ba69a0fde245']='health.d/cpu.conf' + ['343bc919a2fbc93f687f9d1339ec5f79']='health.d/net.conf' + ['35024ebd94542484c0866e6ee6b950cb']='health.d/net.conf' ['3634d5eddc46fb0d50cf47f370670c2c']='health.d/redis.conf' ['364b6e0081b116c9ec073b4d329a6dcc']='health_alarm_notify.conf' ['367d1463e520eb9dc89223bab161c6d1']='python.d/postgres.conf' ['36fdd55665cf10b0db164c2a0cca5e57']='health.d/qos.conf' ['373160658e7d5f1a129de397b9347365']='health.d/entropy.conf' ['373c1276dc9e65884ff2b26e1f08afe7']='health.d/named.conf' + ['3798445a7faaf45c7a8047908678e690']='python.d/varnish.conf' + ['37bc2b50ade9f334da4775dfea59f785']='python.d.conf' ['3848172053221b95279ba9bf789cd4e0']='health.d/apache.conf' ['3866efafd38e161136428d0f818cac43']='health.d/net.conf' ['38d1bf04fe9901481dd6febcc0404a86']='python.d.conf' @@ -89,6 +102,7 @@ declare -A configs_signatures=( ['3c9c47163e9d4dbcb0079b6232398f2f']='apps_groups.conf' ['3ca696189911fb38a0319ddd71e9a395']='python.d/phpfpm.conf' ['3cc6255457d4cba881ae0554ae5d9190']='health.d/squid.conf' + ['3d974ac9fdaa44d4527d6503bec35e34']='stream.conf' ['3f170e3343cd784983b019163393f5af']='health.d/nginx.conf' ['3fc45cc18e884c22482524dff6d27833']='python.d/hddtemp.conf' ['3fcc3c449ce8e0388f9c23ca07cab608']='health.d/backend.conf' @@ -111,6 +125,7 @@ declare -A configs_signatures=( ['4b775fb31342f1478b3773d041a72911']='python.d.conf' ['4ccb06fff1ce06dc5bc80e0a9f568f6e']='charts.d.conf' ['4d13684cadfa90e73ab465409bf7263b']='health.d/mysql.conf' + ['4d91ee6fe4c887ea3865ef36ac63da3c']='health.d/mysql.conf' ['4e995acb0d6fd77403a2a9dca984b55b']='charts.d.conf' ['4f6a5b47a13f5912cc89e9286701dd08']='health.d/redis.conf' ['4f6f4d39c19d7d954f769d3f9d3b4da5']='health.d/memcached.conf' @@ -135,16 +150,20 @@ declare -A configs_signatures=( ['573398335c0c71c075fa57f702bce287']='health.d/disks.conf' ['5829812db29598db5857c9f433e96fef']='python.d/apache.conf' ['58e835b7176865ec5a6f59f7aba832bf']='health.d/named.conf' + ['598f9814966a9e2fe48e8218151d3fa6']='stream.conf' ['5b917d894bb6a755d59264e9d48e9d56']='fping.conf' + ['5bbef0708f5eff4d4a53aaf35fc48a62']='health.d/disks.conf' ['5bf51bb24fb41db9b1e448bd060d3f8c']='apps_groups.conf' ['5da15d6e17a15213a720749045e5d419']='health.d/disks.conf' ['5e6fd588ef6934cf04ddb5e662aa02ea']='health.d/postgres.conf' ['5eb670b6fe39da5fec2523d910b0dd1e']='health.d/cpu.conf' ['5f05d4b248ab2637ada319b4e8c4e4c3']='python.d/varnish.conf' + ['5f109df927d5f20409c81f4bfca0c83e']='python.d/web_log.conf' ['5ff1bcaa58695754e2f6980bfe19f579']='health.d/entropy.conf' ['61b7ed36f35e7bd930f5f7f91694a112']='charts.d/postfix.conf' ['621f10b257a11add5ff5aff41e9662e3']='health.d/memcached.conf' ['623771eecb3c277fc728b5304793f93b']='health.d/cpu.conf' + ['632c28d714c87a4969d11cf36a5edaa8']='health.d/web_log.conf' ['636d032928ea0f4741eab264fb49c099']='apps_groups.conf' ['6398ef37a15cb6a0bc921f58948d2b39']='health.d/softnet.conf' ['64070d856ab1b47a18ec871e49bbc13b']='python.d/squid.conf' @@ -153,6 +172,7 @@ declare -A configs_signatures=( ['64c48f9726ab987baec9c617a9fef7a6']='health.d/nginx.conf' ['64ffc1b6878c81b87564b0f48642c790']='health.d/elasticsearch.conf' ['650b5fc9da23b25ee7ee1481e4aa2851']='health_alarm_notify.conf' + ['653e0c014c8fcfb4db6cd3351d87d720']='python.d.conf' ['6546909d10cc5efcef9dd873bea85956']='python.d/mysql.conf' ['65c6933a17fb6b7f8e6baeab73431c17']='charts.d/apcupsd.conf' ['6608c6546b3c6bde084fc1d34b1163c1']='health.d/retroshare.conf' @@ -163,20 +183,25 @@ declare -A configs_signatures=( ['6a47af861ad3dd112124c37fbf09672b']='apps_groups.conf' ['6b39de5d85db45115db236347a6896d4']='health.d/named.conf' ['6b917300747e7e8314844237e2462261']='python.d/apache_cache.conf' + ['6bb278bd9e171c4cb5c0fe639231288b']='python.d/web_log.conf' ['6bf0de6e3b251b765b10a71d8c5c319d']='python.d/apache.conf' + ['6c9f2f0abe49a6f1a69db052ebcef1bf']='python.d/elasticsearch.conf' ['6ca08ea2a238cad26578b8b85edae160']='health.d/udp_errors.conf' - ['6cba40e32a7e98a98c31a209913839cc']='python.d/nginx_log.conf' ['6d02c2dd0863e09ad9dbba53e3b58116']='health.d/mysql.conf' ['6ea958ca521e0514af57c08b518d8c5c']='health.d/backend.conf' + ['6f303ccfdc21c7b122758cea8c15e249']='python.d.conf' ['70105b1744a8e13f49083d7f1981aea2']='python.d/ipfs.conf' ['707a63f53f4b32e01d134ae90ba94aad']='health_alarm_notify.conf' ['707a63f53f4b32e01d134ae90ba94aad']='health_email_recipients.conf' ['70d82dabecb09a1da4684f293abef0c9']='health_alarm_notify.conf' + ['7120cba2f55b1c0a97a0e10d4f6ef751']='health.d/ipmi.conf' + ['729b3e24a72f7d566fd429617d51a21b']='health.d/web_log.conf' ['73125ae64d5c6e9361944cd9bd14844e']='python.d/exim.conf' ['731a1fcfe9b2da1b9d685056a59541b8']='python.d/hddtemp.conf' ['73a8e10dfe4183aca751e9e2a80dabe3']='node.d.conf' ['7454ed74511d7b9819dfe173f9020786']='python.d/redis.conf' ['749fe31362969d75f1ea66d15231d98d']='python.d/retroshare.conf' + ['7502c931aa9acbb92f54c67978d75983']='stream.conf' ['7596ae54d46ce199ac599429ef753caf']='health.d/cpu.conf' ['75a9c4b0b1c73956df55585eb0619f6c']='charts.d/ap.conf' ['769aa4cdcdc3d78d0328d1f9e4edcdf9']='python.d/mysql.conf' @@ -208,6 +233,8 @@ declare -A configs_signatures=( ['845023f9b4a526aa0e6493756dbe6034']='health.d/squid.conf' ['846ce94bfeeb90c0dc6a89e8d25f1a68']='health.d/named.conf' ['8490f690d97adacc4e2096df82e7e8a5']='charts.d/cpufreq.conf' + ['871bbeea33b83ea9755600b6d574919c']='python.d/web_log.conf' + ['87224d2f2b87646f3c0d38cc1eb30112']='python.d/nsd.conf' ['8810140ce9c09af1d18b9602c4003904']='health_alarm_notify.conf' ['88f77865f75c9fb61c97d700bd4561ee']='python.d/mysql.conf' ['8989b5e2f4ef9cd278ef58be0fae4074']='health.d/disks.conf' @@ -218,6 +245,8 @@ declare -A configs_signatures=( ['8c1d41e2c88aeca78bc319ed74c8748c']='python.d/phpfpm.conf' ['8d0552371a7c9725a04196fa560813d1']='health.d/cpu.conf' ['8dc0bd0a70b5117454bd5f5b98f91c2c']='health.d/disks.conf' + ['8f4f925c1e97dd164007495ec5135ffc']='health.d/fping.conf' + ['8f7b734ea0f89abf8acbb47c50234477']='health.d/web_log.conf' ['8fd472a854b0996327e8ed3562161182']='health_alarm_notify.conf' ['919911d13901d60a7580f5dfd7fc87bb']='health.d/ram.conf' ['91c757ef6be3abdb86906d9dbb9c217a']='fping.conf' @@ -230,15 +259,19 @@ declare -A configs_signatures=( ['99b6030ce25c8fee4598179c0f95fb0b']='health.d/redis.conf' ['99c1617448abbdc493976ab9bda5ce02']='apps_groups.conf' ['9a8a459a3841b78d4c6ef07428ad2fe1']='health.d/entropy.conf' + ['9b6eee7f2febb29efac2b7ea9fcab9be']='charts.d/nut.conf' ['9c0185ceff15415bc59b2ce2c1f04367']='apps_groups.conf' + ['9c8ddfa810d83ae58c8614ee5229e66b']='health.d/disks.conf' ['9c981c75bdf4b1637f7113e7e45eb2bf']='health.d/memcached.conf' ['9e0553ebdc21b64295873fc104cfa79d']='python.d.conf' ['9eb3326ae2ee9badeaad31d8dd2eaa2b']='python.d/isc_dhcpd.conf' ['a02d14124b19c635c1426cee2e98bac5']='charts.d.conf' + ['a03f3e38378385bf87d4c0f81eb1f108']='health.d/tcp_resets.conf' ['a09714b5942cf25a89ec3da1dbc18063']='health.d/ram.conf' ['a0b3a12389c9c56dfe35964b20b59836']='health.d/bind_rndc.conf' ['a0ee8f351f213c0e8af9eb7a4a09cb95']='apps_groups.conf' ['a2944a309f8ce1a3195451856478d6ae']='python.d.conf' + ['a2a647dc492dc2d6ed1f5c0fdc97a96e']='python.d/mongodb.conf' ['a305b400378d6492efd15f9940c2779b']='health.d/softnet.conf' ['a4407787e4beb23a701a8a614dca461d']='health.d/disks.conf' ['a44899a5795bed2863c1d11aa3e85586']='health.d/swap.conf' @@ -262,6 +295,7 @@ declare -A configs_signatures=( ['a9cd91675467c5426f5b51c47602c889']='apps_groups.conf' ['aa4bee249bfc0c4a88ac8c2ffb97aa0d']='health.d/squid.conf' ['aa8b57a733c2035917acf81a8ebdfbe7']='health.d/haproxy.conf' + ['aac44691a1cf95fa8f8990a79bab4ce1']='python.d/web_log.conf' ['abaf2e021f9f6ee5d1c4e4726f47348e']='health.d/ipc.conf' ['acaa6731a272f6d251afb357e99b518f']='apps_groups.conf' ['ade389c1b6efe0cff47c33e662731f0a']='python.d/squid.conf' @@ -285,9 +319,11 @@ declare -A configs_signatures=( ['b8969be5b3ceb4a99477937119bd4323']='python.d.conf' ['b8b87574fd496a66ede884c5336493bd']='python.d/phpfpm.conf' ['b915126262d08aa9da81de539a58a3fb']='python.d/redis.conf' + ['ba11ea2d2f632b2de4b1224bcdc54f07']='python.d/smartd_log.conf' ['bb51112d01ff20053196a57632df8962']='apps_groups.conf' ['bba2f3886587f137ea08a6e63dd3d376']='python.d.conf' ['bda5517ea01640cfdfa0a27549619d6a']='health.d/memcached.conf' + ['bf66f113b2dd8d8fb444cbd5650f284c']='health_alarm_notify.conf' ['c004430f55310ae9ed489c4905ed02cb']='charts.d/apache.conf' ['c080e006f544c949baca33cc24a9c126']='health_alarm_notify.conf' ['c1a7e634b5b8aad523a0d115a93379cd']='health.d/memcached.conf' @@ -323,6 +359,7 @@ declare -A configs_signatures=( ['d8dc489e32f7114c6298fce94e86a8ef']='health.d/entropy.conf' ['d9036091e2232fc2b8bfa8c7484dea28']='apps_groups.conf' ['d9258e671d0d0b6498af1ce16ef030d2']='apps_groups.conf' + ['d9fa0290cdfe4153188bb52dd31191df']='apps_groups.conf' ['da29d2ab1ab7b8fda189960c840e5144']='health.d/swap.conf' ['dad303c5cca7a69345811a01a74f5892']='health.d/net.conf' ['dc0d2b96378f290eec3fcf98b89ad824']='python.d/cpufreq.conf' @@ -334,11 +371,14 @@ declare -A configs_signatures=( ['de02f899a61f21b86adb646940f0bcae']='health.d/net.conf' ['def883f35986c9d25de63b1a8e7d0f46']='health.d/entropy.conf' ['df381f3a7ca9fb2b4b43ae7cb7a4c492']='python.d/mysql.conf' + ['df7e8044902b5e155fad8430c2ddcfa8']='health.d/fping.conf' ['dfd5431b11cf2f3852a40d390c1d5a92']='python.d/varnish.conf' ['e0242003fd2e3f9ac1b9314e802ada79']='python.d/hddtemp.conf' + ['e0ba3bc216ffc9933b4741dbb6b1f8c8']='health.d/web_log.conf' ['e0e96cc47ed61d6492416be5236cd4d3']='python.d/apache_cache.conf' ['e2f3388c06726154c10ec22bad5bc7ec']='fping.conf' ['e3023092e3b2bbb5351e0fe6682f4fe9']='health_alarm_notify.conf' + ['e3d100c2d0347c08efbf6245e05620c6']='python.d/fail2ban.conf' ['e3e5bc57335c489f01b8559f5c70e112']='python.d/squid.conf' ['e40947d22f7ed5359f12fc89e3512963']='python.d/dovecot.conf' ['e449e5582279742496550df14b6fca95']='health.d/entropy.conf' @@ -346,24 +386,28 @@ declare -A configs_signatures=( ['e734c5951a8764d4d9de046dd7cf7407']='health.d/softnet.conf' ['e7bc22a1942cffbd2b1b0cfd119ee328']='health.d/ipfs.conf' ['e8ec8046c7007af6ca3e8c51e62c99f8']='health.d/disks.conf' + ['ea1a96c42ad464c354fb250e3408c3e8']='stream.conf' + ['eaa7beb935cae9c48a40fb934eb105a7']='health.d/web_log.conf' ['eb5168f0b516bc982aac45e59da6e52e']='health.d/nginx.conf' ['eb748d6fb69d11b0d29c5794657e206c']='health.d/qos.conf' ['ebd0612ccc5807524ebb2b647e3e56c9']='apps_groups.conf' - ['ec490e037c1e53daa44a55a941381d6d']='python.d/gunicorn_log.conf' ['ecd3aa97e2581f88eb466d6612690ef2']='charts.d/nginx.conf' ['ee5343881744e6a97e6ee5cdd329cfb8']='health.d/retroshare.conf' ['ef1861bf5725d91e773cbdba05687597']='python.d.conf' ['ef9916ea144878a9f37cbb6b1b29da10']='health.d/squid.conf' + ['f1446cb3f1a905ee06defa2aa15ee806']='python.d/web_log.conf' ['f2f1b8656f5011e965ac45b818cf668d']='apps_groups.conf' ['f42df9f13abfae2426519c6728b34882']='charts.d/example.conf' ['f4c5d88c34d3fb853498124177cc77f1']='python.d.conf' ['f5736e0b2945182cb659cb0713eff923']='apps_groups.conf' ['f66e5236ba1245bb2e5fd99191f114c6']='charts.d/hddtemp.conf' ['f6c6656f900ff52d159dca12d624016a']='python.d/postgres.conf' + ['f7401a6e7c7d4fe2e0e2be7f7f523275']='health.d/web_log.conf' ['f7a99e94231beda85c6254912d8d31c1']='python.d/tomcat.conf' ['f82924563e41d99cdae5431f0af69155']='python.d.conf' ['f8c30f22df92765e2c0fab3c8174e2fc']='health.d/memcached.conf' ['f8dade4484f1b6a48655388502df7d5a']='health_alarm_notify.conf' + ['f96acba4b14b0c1b50d0187a04416151']='health_alarm_notify.conf' ['f9be549a849d023595d19d5d74263e0f']='health.d/tcp_resets.conf' ['fa4396513b358d6ec6a7f5bfb08439b8']='health.d/net.conf' ['fd3164e6e8cb6726706267eae49aa082']='health_alarm_notify.conf' diff --git a/configure b/configure index 39644b13a..f638780b4 100755 --- a/configure +++ b/configure @@ -1,9 +1,11 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for netdata 1.5.0. +# Generated by GNU Autoconf 2.68 for netdata 1.6.0. # # -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software +# Foundation, Inc. # # # This configure script is free software; the Free Software Foundation @@ -132,31 +134,6 @@ export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh @@ -190,8 +167,7 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" +test x\$exitcode = x0 || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && @@ -236,25 +212,21 @@ IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + # Preserve -v and -x to the replacement shell. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; + esac + exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then : @@ -356,14 +328,6 @@ $as_echo X"$as_dir" | } # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take @@ -485,10 +449,6 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). @@ -523,16 +483,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' + as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -544,8 +504,28 @@ else as_mkdir_p=false fi -as_test_x='test -x' -as_executable_p=as_fn_executable_p +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -577,8 +557,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='netdata' PACKAGE_TARNAME='netdata' -PACKAGE_VERSION='1.5.0' -PACKAGE_STRING='netdata 1.5.0' +PACKAGE_VERSION='1.6.0' +PACKAGE_STRING='netdata 1.6.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -620,10 +600,15 @@ ac_includes_default="\ #endif" ac_func_list= +ac_header_list= ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +OPTIONAL_IPMIMONITORING_LIBS +OPTIONAL_IPMIMONITORING_CFLAGS +OPTIONAL_LIBCAP_LIBS +OPTIONAL_LIBCAP_CFLAGS OPTIONAL_UUID_LIBS OPTIONAL_UUID_CLFAGS OPTIONAL_ZLIB_LIBS @@ -642,23 +627,37 @@ chartsdir cachedir registrydir varlibdir -ZLIB_LIBS -ZLIB_CFLAGS +ENABLE_PLUGIN_NFACCT_FALSE +ENABLE_PLUGIN_NFACCT_TRUE LIBMNL_LIBS LIBMNL_CFLAGS NFACCT_LIBS NFACCT_CFLAGS +ENABLE_PLUGIN_FREEIPMI_FALSE +ENABLE_PLUGIN_FREEIPMI_TRUE +IPMIMONITORING_LIBS +IPMIMONITORING_CFLAGS +ENABLE_PLUGIN_APPS_FALSE +ENABLE_PLUGIN_APPS_TRUE +ENABLE_CAPABILITY_FALSE +ENABLE_CAPABILITY_TRUE +LIBCAP_LIBS +LIBCAP_CFLAGS +has_tcmalloc +has_jemalloc +SSE_CANDIDATE UUID_LIBS UUID_CFLAGS +ZLIB_LIBS +ZLIB_CFLAGS MATH_LIBS MATH_CFLAGS -SSE_CANDIDATE -has_tcmalloc -has_jemalloc PTHREAD_CFLAGS PTHREAD_LIBS PTHREAD_CC ax_pthread_config +LINUX_FALSE +LINUX_TRUE MACOS_FALSE MACOS_TRUE FREEBSD_FALSE @@ -669,18 +668,6 @@ CPP PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -AM_BACKSLASH -AM_DEFAULT_VERBOSITY -AM_DEFAULT_V -AM_V am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE @@ -691,6 +678,21 @@ AMDEP_TRUE am__quote am__include DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build am__untar am__tar AMTAR @@ -714,13 +716,6 @@ am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC PACKAGE_RPM_RELEASE PACKAGE_RPM_VERSION MAINT @@ -769,14 +764,16 @@ ac_user_opts=' enable_option_checking enable_maintainer_mode enable_dependency_tracking -enable_silent_rules enable_plugin_nfacct +enable_plugin_freeipmi enable_pedantic with_webdir +with_libcap with_zlib with_math with_user enable_x86_sse +enable_lto with_jemalloc_prefix with_jemalloc with_tcmalloc_lib @@ -794,17 +791,21 @@ PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR CPP -SSE_CANDIDATE MATH_CFLAGS MATH_LIBS +ZLIB_CFLAGS +ZLIB_LIBS UUID_CFLAGS UUID_LIBS +SSE_CANDIDATE +LIBCAP_CFLAGS +LIBCAP_LIBS +IPMIMONITORING_CFLAGS +IPMIMONITORING_LIBS NFACCT_CFLAGS NFACCT_LIBS LIBMNL_CFLAGS -LIBMNL_LIBS -ZLIB_CFLAGS -ZLIB_LIBS' +LIBMNL_LIBS' # Initialize some variables set by options. @@ -1260,6 +1261,8 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe + $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1345,7 +1348,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures netdata 1.5.0 to adapt to many kinds of systems. +\`configure' configures netdata 1.6.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1415,7 +1418,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of netdata 1.5.0:";; + short | recursive ) echo "Configuration of netdata 1.6.0:";; esac cat <<\_ACEOF @@ -1423,23 +1426,22 @@ Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-maintainer-mode - enable make rules and dependencies not useful (and - sometimes confusing) to the casual installer - --enable-dependency-tracking - do not reject slow dependency extractors - --disable-dependency-tracking - speeds up one-time build - --enable-silent-rules less verbose build output (undo: "make V=1") - --disable-silent-rules verbose build output (undo: "make V=0") + --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors --enable-plugin-nfacct enable nfacct plugin, requires root + --enable-plugin-freeipmi + enable freeipmi plugin --enable-pedantic enable pedantic compiler warnings --disable-x86-sse SSE/SS2 optimizations on x86 [default enabled] + --disable-lto Link Time Optimizations [default enabled] Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-webdir location of webdir [PKGDATADIR/web] + --with-libcap build with libcap --with-zlib build with zlib --with-math build with math --with-user use this user to drop privilege @@ -1464,20 +1466,27 @@ Some influential environment variables: PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path CPP C preprocessor - SSE_CANDIDATE - C compiler flags for SSE MATH_CFLAGS C compiler flags for math MATH_LIBS linker flags for math + ZLIB_CFLAGS C compiler flags for ZLIB, overriding pkg-config + ZLIB_LIBS linker flags for ZLIB, overriding pkg-config UUID_CFLAGS C compiler flags for UUID, overriding pkg-config UUID_LIBS linker flags for UUID, overriding pkg-config + SSE_CANDIDATE + C compiler flags for SSE + LIBCAP_CFLAGS + C compiler flags for LIBCAP, overriding pkg-config + LIBCAP_LIBS linker flags for LIBCAP, overriding pkg-config + IPMIMONITORING_CFLAGS + C compiler flags for IPMIMONITORING, overriding pkg-config + IPMIMONITORING_LIBS + linker flags for IPMIMONITORING, overriding pkg-config NFACCT_CFLAGS C compiler flags for NFACCT, overriding pkg-config NFACCT_LIBS linker flags for NFACCT, overriding pkg-config LIBMNL_CFLAGS C compiler flags for LIBMNL, overriding pkg-config LIBMNL_LIBS linker flags for LIBMNL, overriding pkg-config - ZLIB_CFLAGS C compiler flags for ZLIB, overriding pkg-config - ZLIB_LIBS linker flags for ZLIB, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -1545,10 +1554,10 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -netdata configure 1.5.0 -generated by GNU Autoconf 2.69 +netdata configure 1.6.0 +generated by GNU Autoconf 2.68 -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1597,52 +1606,6 @@ fi } # ac_fn_c_try_compile -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_link - # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. @@ -1840,6 +1803,52 @@ $as_echo "$ac_res" >&6; } } # ac_fn_c_check_header_compile +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly @@ -1961,6 +1970,80 @@ $as_echo "$ac_res" >&6; } } # ac_fn_c_check_type +# ac_fn_c_find_intX_t LINENO BITS VAR +# ----------------------------------- +# Finds a signed integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_intX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 +$as_echo_n "checking for int$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in int$2_t 'int' 'long int' \ + 'long long int' 'short int' 'signed char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) + < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + case $ac_type in #( + int$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_intX_t + # ac_fn_c_find_uintX_t LINENO BITS VAR # ------------------------------------ # Finds an unsigned integer type with width BITS, setting cache variable VAR @@ -1985,8 +2068,7 @@ int main () { static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2078,8 +2160,7 @@ int main () { static int test_array [1 - 2 * !(($2) >= 0)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2095,8 +2176,7 @@ int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2122,8 +2202,7 @@ int main () { static int test_array [1 - 2 * !(($2) < 0)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2139,8 +2218,7 @@ int main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2174,8 +2252,7 @@ int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2247,8 +2324,8 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by netdata $as_me 1.5.0, which was -generated by GNU Autoconf 2.69. Invocation command line was +It was created by netdata $as_me 1.6.0, which was +generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -2528,6 +2605,8 @@ $as_echo "$as_me: creating cache $cache_file" >&6;} fi as_fn_append ac_func_list " accept4" +as_fn_append ac_header_list " sys/prctl.h" +as_fn_append ac_header_list " linux/netfilter/nfnetlink_conntrack.h" # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false @@ -2626,15 +2705,16 @@ $as_echo "$as_me: ***************** MAINTAINER MODE *****************" >&6;} PACKAGE_BUILT_DATE=$(date '+%d %b %Y') fi -PACKAGE_RPM_VERSION="1.5.0" +PACKAGE_RPM_VERSION="1.6.0" -# fails on centos6 -#AX_CHECK_ENABLE_DEBUG() + +# ----------------------------------------------------------------------------- +# autoconf initialization ac_aux_dir= -for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do +for ac_dir in . "$srcdir"/.; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" @@ -2650,7 +2730,7 @@ for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do fi done if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 + as_fn_error $? "cannot find install-sh, install.sh, or shtool in . \"$srcdir\"/." "$LINENO" 5 fi # These three variables are undocumented and unsupported, @@ -2662,33 +2742,220 @@ ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. -# Expand $ac_aux_dir to an absolute path. -am_aux_dir=`cd "$ac_aux_dir" && pwd` +ac_config_headers="$ac_config_headers config.h" -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : + + +am__api_version='1.11' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Just in case +sleep 1 +echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;; +esac + +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken +alias in your environment" "$LINENO" 5 + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi @@ -2698,10 +2965,10 @@ IFS=$as_save_IFS fi fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } @@ -2709,17 +2976,17 @@ fi fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH @@ -2727,8 +2994,8 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi @@ -2738,17 +3005,17 @@ IFS=$as_save_IFS fi fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi - if test "x$ac_ct_CC" = x; then - CC="" + if test "x$ac_ct_STRIP" = x; then + STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) @@ -2756,23 +3023,74 @@ yes:) $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac - CC=$ac_ct_CC + STRIP=$ac_ct_STRIP fi else - CC="$ac_cv_prog_CC" + STRIP="$ac_cv_prog_STRIP" fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +mkdir_p="$MKDIR_P" +case $mkdir_p in + [\\/$]* | ?:[\\/]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : +if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH @@ -2780,8 +3098,8 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi @@ -2791,80 +3109,200 @@ IFS=$as_save_IFS fi fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ fi +rmdir .tst 2>/dev/null + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + + +# Define the identity of the package. + PACKAGE='netdata' + VERSION='1.6.0' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : @@ -2879,8 +3317,8 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi @@ -2900,15 +3338,11 @@ $as_echo "no" >&6; } fi - test -n "$CC" && break - done fi -if test -z "$CC"; then +if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : @@ -2923,8 +3357,8 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi @@ -2943,10 +3377,6 @@ else $as_echo "no" >&6; } fi - - test -n "$ac_ct_CC" && break -done - if test "x$ac_ct_CC" = x; then CC="" else @@ -2958,17 +3388,217 @@ ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi +else + CC="$ac_cv_prog_CC" fi -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 @@ -3369,7 +3999,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include -struct stat; +#include +#include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -3453,113 +4084,195 @@ ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu +DEPDIR="${am__leading_dot}deps" -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 -$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } -if ${am_cv_prog_cc_c_o+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ +ac_config_commands="$ac_config_commands depfiles" -int -main () -{ - ; - return 0; -} -_ACEOF - # Make sure it works both with $CC and with simple cc. - # Following AC_PROG_CC_C_O, we do the test twice because some - # compilers refuse to overwrite an existing .o file with -o, - # though they will create one. - am_cv_prog_cc_c_o=yes - for am_i in 1 2; do - if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 - ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } \ - && test -f conftest2.$ac_objext; then - : OK - else - am_cv_prog_cc_c_o=no - break - fi - done - rm -f core conftest* - unset am_i -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 -$as_echo "$am_cv_prog_cc_c_o" >&6; } -if test "$am_cv_prog_cc_c_o" != yes; then - # Losing compiler, so override with the script. - # FIXME: It is wrong to rewrite CC. - # But if we don't then we get into trouble of one sort or another. - # A longer-term fix would be to have automake use am__CC in this case, - # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" - CC="$am_aux_dir/compile $CC" +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((returns_nonnull))" >&5 -$as_echo_n "checking for __attribute__((returns_nonnull))... " >&6; } -if ${ax_cv_have_func_attribute_returns_nonnull+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ +depcc="$CC" am_compiler_list= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub - void *foo( void ) __attribute__((returns_nonnull)); + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac -int -main () -{ + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - ; - return 0; -} + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - if test -s conftest.err; then : - ax_cv_have_func_attribute_returns_nonnull=no -else - ax_cv_have_func_attribute_returns_nonnull=yes -fi + cd .. + rm -rf conftest.dir else - ax_cv_have_func_attribute_returns_nonnull=no + am_cv_CC_dependencies_compiler_type=none fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_returns_nonnull" >&5 -$as_echo "$ax_cv_have_func_attribute_returns_nonnull" >&6; } - - if test yes = $ax_cv_have_func_attribute_returns_nonnull; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_FUNC_ATTRIBUTE_RETURNS_NONNULL 1 -_ACEOF +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= fi @@ -3567,1452 +4280,745 @@ fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((malloc))" >&5 -$as_echo_n "checking for __attribute__((malloc))... " >&6; } -if ${ax_cv_have_func_attribute_malloc+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - void *foo( void ) __attribute__((malloc)); - -int -main () -{ - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - if test -s conftest.err; then : - ax_cv_have_func_attribute_malloc=no +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 else - ax_cv_have_func_attribute_malloc=yes + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } else - ax_cv_have_func_attribute_malloc=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_malloc" >&5 -$as_echo "$ax_cv_have_func_attribute_malloc" >&6; } - - if test yes = $ax_cv_have_func_attribute_malloc; then : -cat >>confdefs.h <<_ACEOF -#define HAVE_FUNC_ATTRIBUTE_MALLOC 1 -_ACEOF fi - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((noreturn))" >&5 -$as_echo_n "checking for __attribute__((noreturn))... " >&6; } -if ${ax_cv_have_func_attribute_noreturn+:} false; then : +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - - void foo( void ) __attribute__((noreturn)); - -int -main () -{ - - ; - return 0; -} - -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - if test -s conftest.err; then : - ax_cv_have_func_attribute_noreturn=no -else - ax_cv_have_func_attribute_noreturn=yes + ;; +esac fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } else - ax_cv_have_func_attribute_noreturn=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_noreturn" >&5 -$as_echo "$ax_cv_have_func_attribute_noreturn" >&6; } - - if test yes = $ax_cv_have_func_attribute_noreturn; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_FUNC_ATTRIBUTE_NORETURN 1 -_ACEOF fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((format))" >&5 -$as_echo_n "checking for __attribute__((format))... " >&6; } -if ${ax_cv_have_func_attribute_format+:} false; then : +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - - - int foo(const char *p, ...) __attribute__((format(printf, 1, 2))); - -int -main () -{ - - ; - return 0; -} - +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error _ACEOF -if ac_fn_c_try_link "$LINENO"; then : - if test -s conftest.err; then : - ax_cv_have_func_attribute_format=no +if ac_fn_c_try_cpp "$LINENO"; then : + else - ax_cv_have_func_attribute_format=yes + # Broken: fails on valid input. +continue fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue else - ax_cv_have_func_attribute_format=no + # Passes both tests. +ac_preproc_ok=: +break fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_format" >&5 -$as_echo "$ax_cv_have_func_attribute_format" >&6; } - - if test yes = $ax_cv_have_func_attribute_format; then : -cat >>confdefs.h <<_ACEOF -#define HAVE_FUNC_ATTRIBUTE_FORMAT 1 -_ACEOF + done + ac_cv_prog_CPP=$CPP fi - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((warn_unused_result))" >&5 -$as_echo_n "checking for __attribute__((warn_unused_result))... " >&6; } -if ${ax_cv_have_func_attribute_warn_unused_result+:} false; then : - $as_echo_n "(cached) " >&6 + CPP=$ac_cv_prog_CPP else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext - int foo( void ) __attribute__((warn_unused_result)); - -int -main () -{ - - ; - return 0; -} - + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include _ACEOF -if ac_fn_c_try_link "$LINENO"; then : - if test -s conftest.err; then : - ax_cv_have_func_attribute_warn_unused_result=no +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue else - ax_cv_have_func_attribute_warn_unused_result=yes + # Passes both tests. +ac_preproc_ok=: +break fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + else - ax_cv_have_func_attribute_warn_unused_result=no + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_warn_unused_result" >&5 -$as_echo "$ax_cv_have_func_attribute_warn_unused_result" >&6; } +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu - if test yes = $ax_cv_have_func_attribute_warn_unused_result; then : -cat >>confdefs.h <<_ACEOF -#define HAVE_FUNC_ATTRIBUTE_WARN_UNUSED_RESULT 1 -_ACEOF - -fi - - - - -ac_aux_dir= -for ac_dir in . "$srcdir"/.; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in . \"$srcdir\"/." "$LINENO" 5 -fi - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - -ac_config_headers="$ac_config_headers config.h" - - - -am__api_version='1.15' - -# Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -# Reject install programs that cannot install multiple files. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 -$as_echo_n "checking for a BSD-compatible install... " >&6; } -if test -z "$INSTALL"; then -if ${ac_cv_path_install+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in #(( - ./ | .// | /[cC]/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ - /usr/ucb/* ) ;; - *) - # OSF1 and SCO ODT 3.0 have their own names for install. - # Don't use installbsd from OSF since it installs stuff as root - # by default. - for ac_prog in ginstall scoinst install; do - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then - if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # AIX install. It has an incompatible calling convention. - : - elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # program-specific install script used by HP pwplus--don't use. - : - else - rm -rf conftest.one conftest.two conftest.dir - echo one > conftest.one - echo two > conftest.two - mkdir conftest.dir - if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && - test -s conftest.one && test -s conftest.two && - test -s conftest.dir/conftest.one && - test -s conftest.dir/conftest.two - then - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" - break 3 - fi - fi - fi - done - done - ;; + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac + $ac_path_GREP_found && break 3 + done + done done IFS=$as_save_IFS - -rm -rf conftest.one conftest.two conftest.dir - -fi - if test "${ac_cv_path_install+set}" = set; then - INSTALL=$ac_cv_path_install - else - # As a last resort, use the slow shell script. Don't cache a - # value for INSTALL within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - INSTALL=$ac_install_sh + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 -$as_echo "$INSTALL" >&6; } - -# Use test -z because SunOS4 sh mishandles braces in ${var-val}. -# It thinks the first close brace ends the variable substitution. -test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' - -test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' - -test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 -$as_echo_n "checking whether build environment is sane... " >&6; } -# Reject unsafe characters in $srcdir or the absolute working directory -# name. Accept space and tab only in the latter. -am_lf=' -' -case `pwd` in - *[\\\"\#\$\&\'\`$am_lf]*) - as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; -esac -case $srcdir in - *[\\\"\#\$\&\'\`$am_lf\ \ ]*) - as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; -esac - -# Do 'set' in a subshell so we don't clobber the current shell's -# arguments. Must try -L first in case configure is actually a -# symlink; some systems play weird games with the mod time of symlinks -# (eg FreeBSD returns the mod time of the symlink's containing -# directory). -if ( - am_has_slept=no - for am_try in 1 2; do - echo "timestamp, slept: $am_has_slept" > conftest.file - set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` - if test "$*" = "X"; then - # -L didn't work. - set X `ls -t "$srcdir/configure" conftest.file` - fi - if test "$*" != "X $srcdir/configure conftest.file" \ - && test "$*" != "X conftest.file $srcdir/configure"; then - - # If neither matched, then we have a broken ls. This can happen - # if, for instance, CONFIG_SHELL is bash and it inherits a - # broken ls alias from the environment. This has actually - # happened. Such a system could not be considered "sane". - as_fn_error $? "ls -t appears to fail. Make sure there is not a broken - alias in your environment" "$LINENO" 5 - fi - if test "$2" = conftest.file || test $am_try -eq 2; then - break - fi - # Just in case. - sleep 1 - am_has_slept=yes - done - test "$2" = conftest.file - ) -then - # Ok. - : else - as_fn_error $? "newly created file is older than distributed files! -Check your system clock" "$LINENO" 5 -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -# If we didn't sleep, we still need to ensure time stamps of config.status and -# generated files are strictly newer. -am_sleep_pid= -if grep 'slept: no' conftest.file >/dev/null 2>&1; then - ( sleep 1 ) & - am_sleep_pid=$! + ac_cv_path_GREP=$GREP fi -rm -f conftest.file - -test "$program_prefix" != NONE && - program_transform_name="s&^&$program_prefix&;$program_transform_name" -# Use a double $ so make ignores it. -test "$program_suffix" != NONE && - program_transform_name="s&\$&$program_suffix&;$program_transform_name" -# Double any \ or $. -# By default was `s,x,x', remove it if useless. -ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' -program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` - -if test x"${MISSING+set}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; - *) - MISSING="\${SHELL} $am_aux_dir/missing" ;; - esac -fi -# Use eval to expand $SHELL -if eval "$MISSING --is-lightweight"; then - am_missing_run="$MISSING " -else - am_missing_run= - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 -$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" -if test x"${install_sh+set}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; - *) - install_sh="\${SHELL} $am_aux_dir/install-sh" - esac -fi -# Installed binaries are usually stripped using 'strip' when the user -# run "make install-strip". However 'strip' might not be the right -# tool to use in cross-compilation environments, therefore Automake -# will honor the 'STRIP' environment variable to overrule this program. -if test "$cross_compiling" != no; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. -set dummy ${ac_tool_prefix}strip; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_STRIP+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$STRIP"; then - ac_cv_prog_STRIP="$STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_STRIP="${ac_tool_prefix}strip" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -STRIP=$ac_cv_prog_STRIP -if test -n "$STRIP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 -$as_echo "$STRIP" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_STRIP"; then - ac_ct_STRIP=$STRIP - # Extract the first word of "strip", so it can be a program name with args. -set dummy strip; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_STRIP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_STRIP"; then - ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_STRIP="strip" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break done -IFS=$as_save_IFS - -fi -fi -ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP -if test -n "$ac_ct_STRIP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 -$as_echo "$ac_ct_STRIP" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_STRIP" = x; then - STRIP=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac - STRIP=$ac_ct_STRIP - fi -else - STRIP="$ac_cv_prog_STRIP" -fi -fi -INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 -$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } -if test -z "$MKDIR_P"; then - if ${ac_cv_path_mkdir+:} false; then : - $as_echo_n "(cached) " >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in mkdir gmkdir; do - for ac_exec_ext in '' $ac_executable_extensions; do - as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue - case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( - 'mkdir (GNU coreutils) '* | \ - 'mkdir (coreutils) '* | \ - 'mkdir (fileutils) '4.1*) - ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext - break 3;; - esac - done - done + $ac_path_EGREP_found && break 3 + done done -IFS=$as_save_IFS - -fi - - test -d ./--version && rmdir ./--version - if test "${ac_cv_path_mkdir+set}" = set; then - MKDIR_P="$ac_cv_path_mkdir -p" - else - # As a last resort, use the slow shell script. Don't cache a - # value for MKDIR_P within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - MKDIR_P="$ac_install_sh -d" - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 -$as_echo "$MKDIR_P" >&6; } - -for ac_prog in gawk mawk nawk awk -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AWK+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$AWK"; then - ac_cv_prog_AWK="$AWK" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_AWK="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done done IFS=$as_save_IFS - -fi -fi -AWK=$ac_cv_prog_AWK -if test -n "$AWK"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 -$as_echo "$AWK" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$AWK" && break -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 -$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } -set x ${MAKE-make} -ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat >conftest.make <<\_ACEOF -SHELL = /bin/sh -all: - @echo '@@@%%%=$(MAKE)=@@@%%%' -_ACEOF -# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. -case `${MAKE-make} -f conftest.make 2>/dev/null` in - *@@@%%%=?*=@@@%%%*) - eval ac_cv_prog_make_${ac_make}_set=yes;; - *) - eval ac_cv_prog_make_${ac_make}_set=no;; -esac -rm -f conftest.make -fi -if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - SET_MAKE= -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - SET_MAKE="MAKE=${MAKE-make}" -fi - -rm -rf .tst 2>/dev/null -mkdir .tst 2>/dev/null -if test -d .tst; then - am__leading_dot=. -else - am__leading_dot=_ -fi -rmdir .tst 2>/dev/null - -DEPDIR="${am__leading_dot}deps" - -ac_config_commands="$ac_config_commands depfiles" - - -am_make=${MAKE-make} -cat > confinc << 'END' -am__doit: - @echo this is the am__doit target -.PHONY: am__doit -END -# If we don't find an include directive, just comment out the code. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 -$as_echo_n "checking for style of include used by $am_make... " >&6; } -am__include="#" -am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 -$as_echo "$_am_result" >&6; } -rm -f confinc confmf - -# Check whether --enable-dependency-tracking was given. -if test "${enable_dependency_tracking+set}" = set; then : - enableval=$enable_dependency_tracking; -fi - -if test "x$enable_dependency_tracking" != xno; then - am_depcomp="$ac_aux_dir/depcomp" - AMDEPBACKSLASH='\' - am__nodep='_no' -fi - if test "x$enable_dependency_tracking" != xno; then - AMDEP_TRUE= - AMDEP_FALSE='#' -else - AMDEP_TRUE='#' - AMDEP_FALSE= -fi - - -# Check whether --enable-silent-rules was given. -if test "${enable_silent_rules+set}" = set; then : - enableval=$enable_silent_rules; -fi - -case $enable_silent_rules in # ((( - yes) AM_DEFAULT_VERBOSITY=0;; - no) AM_DEFAULT_VERBOSITY=1;; - *) AM_DEFAULT_VERBOSITY=1;; -esac -am_make=${MAKE-make} -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 -$as_echo_n "checking whether $am_make supports nested variables... " >&6; } -if ${am_cv_make_support_nested_variables+:} false; then : - $as_echo_n "(cached) " >&6 -else - if $as_echo 'TRUE=$(BAR$(V)) -BAR0=false -BAR1=true -V=1 -am__doit: - @$(TRUE) -.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then - am_cv_make_support_nested_variables=yes -else - am_cv_make_support_nested_variables=no -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 -$as_echo "$am_cv_make_support_nested_variables" >&6; } -if test $am_cv_make_support_nested_variables = yes; then - AM_V='$(V)' - AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' -else - AM_V=$AM_DEFAULT_VERBOSITY - AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY -fi -AM_BACKSLASH='\' - -if test "`cd $srcdir && pwd`" != "`pwd`"; then - # Use -I$(srcdir) only when $(srcdir) != ., so that make's output - # is not polluted with repeated "-I." - am__isrc=' -I$(srcdir)' - # test to see if srcdir already configured - if test -f $srcdir/config.status; then - as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 - fi -fi - -# test whether we have cygpath -if test -z "$CYGPATH_W"; then - if (cygpath --version) >/dev/null 2>/dev/null; then - CYGPATH_W='cygpath -w' - else - CYGPATH_W=echo - fi -fi - - -# Define the identity of the package. - PACKAGE='netdata' - VERSION='1.5.0' - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE "$PACKAGE" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define VERSION "$VERSION" -_ACEOF - -# Some tools Automake needs. - -ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} - - -AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} - - -AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} - - -AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} - - -MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} - -# For better backward compatibility. To be removed once Automake 1.9.x -# dies out for good. For more background, see: -# -# -mkdir_p='$(MKDIR_P)' - -# We need awk for the "check" target (and possibly the TAP driver). The -# system "awk" is bad on some platforms. -# Always define AMTAR for backward compatibility. Yes, it's still used -# in the wild :-( We should find a proper way to deprecate it ... -AMTAR='$${TAR-tar}' - - -# We'll loop over all known methods to create a tar archive until one works. -_am_tools='gnutar pax cpio none' - -am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' - - - - - -depcc="$CC" am_compiler_list= - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 -$as_echo_n "checking dependency style of $depcc... " >&6; } -if ${am_cv_CC_dependencies_compiler_type+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then - # We make a subdir and do the tests there. Otherwise we can end up - # making bogus files that we don't know about and never remove. For - # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named 'D' -- because '-MD' means "put the output - # in D". - rm -rf conftest.dir - mkdir conftest.dir - # Copy depcomp to subdir because otherwise we won't find it if we're - # using a relative directory. - cp "$am_depcomp" conftest.dir - cd conftest.dir - # We will build objects and dependencies in a subdirectory because - # it helps to detect inapplicable dependency modes. For instance - # both Tru64's cc and ICC support -MD to output dependencies as a - # side effect of compilation, but ICC will put the dependencies in - # the current directory while Tru64 will put them in the object - # directory. - mkdir sub - - am_cv_CC_dependencies_compiler_type=none - if test "$am_compiler_list" = ""; then - am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` - fi - am__universal=false - case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac - - for depmode in $am_compiler_list; do - # Setup a source with many dependencies, because some compilers - # like to wrap large dependency lists on column 80 (with \), and - # we should not choose a depcomp mode which is confused by this. - # - # We need to recreate these files for each test, as the compiler may - # overwrite some of them when testing with obscure command lines. - # This happens at least with the AIX C compiler. - : > sub/conftest.c - for i in 1 2 3 4 5 6; do - echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with - # Solaris 10 /bin/sh. - echo '/* dummy */' > sub/conftst$i.h - done - echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - - # We check with '-c' and '-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle '-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs. - am__obj=sub/conftest.${OBJEXT-o} - am__minus_obj="-o $am__obj" - case $depmode in - gcc) - # This depmode causes a compiler race in universal mode. - test "$am__universal" = false || continue - ;; - nosideeffect) - # After this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested. - if test "x$enable_dependency_tracking" = xyes; then - continue - else - break - fi - ;; - msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok '-c -o', but also, the minuso test has - # not run yet. These depmodes are late enough in the game, and - # so weak that their functioning should not be impacted. - am__obj=conftest.${OBJEXT-o} - am__minus_obj= - ;; - none) break ;; - esac - if depmode=$depmode \ - source=sub/conftest.c object=$am__obj \ - depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ - >/dev/null 2>conftest.err && - grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep $am__obj sub/conftest.Po > /dev/null 2>&1 && - ${MAKE-make} -s -f confmf > /dev/null 2>&1; then - # icc doesn't choke on unknown options, it will just issue warnings - # or remarks (even with -Werror). So we grep stderr for any message - # that says an option was ignored or not supported. - # When given -MP, icc 7.0 and 7.1 complain thusly: - # icc: Command line warning: ignoring option '-M'; no argument required - # The diagnosis changed in icc 8.0: - # icc: Command line remark: option '-MP' not supported - if (grep 'ignoring option' conftest.err || - grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else - am_cv_CC_dependencies_compiler_type=$depmode - break - fi - fi - done - - cd .. - rm -rf conftest.dir + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi else - am_cv_CC_dependencies_compiler_type=none + ac_cv_path_EGREP=$EGREP fi + fi fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 -$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } -CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" - if - test "x$enable_dependency_tracking" != xno \ - && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then - am__fastdepCC_TRUE= - am__fastdepCC_FALSE='#' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 else - am__fastdepCC_TRUE='#' - am__fastdepCC_FALSE= -fi + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +int +main () +{ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -# POSIX will say in a future version that running "rm -f" with no argument -# is OK; and we want to be able to make that assumption in our Makefile -# recipes. So use an aggressive probe to check that the usage we want is -# actually supported "in the wild" to an acceptable degree. -# See automake bug#10828. -# To make any issue more visible, cause the running configure to be aborted -# by default if the 'rm' program in use doesn't match our expectations; the -# user can still override this though. -if rm -f && rm -fr && rm -rf; then : OK; else - cat >&2 <<'END' -Oops! +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include -Your 'rm' program seems unable to run without file operands specified -on the command line, even when the '-f' option is present. This is contrary -to the behaviour of most rm programs out there, and not conforming with -the upcoming POSIX standard: +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : -Please tell bug-automake@gnu.org about your system, including the value -of your $PATH and any error possibly output before this message. This -can help us improve future automake versions. +else + ac_cv_header_stdc=no +fi +rm -f conftest* -END - if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then - echo 'Configuration will proceed anyway, since you have set the' >&2 - echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 - echo >&2 - else - cat >&2 <<'END' -Aborting the configuration process, to ensure you take notice of the issue. +fi -You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include -If you want to complete the configuration process using your problematic -'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM -to "yes", and re-run configure. +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : -END - as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 - fi +else + ac_cv_header_stdc=no fi +rm -f conftest* -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 +fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 + ac_cv_header_stdc=no fi - +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS fi fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + fi +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi + done - done -IFS=$as_save_IFS -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } + + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = xyes; then : + MINIX=yes else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + MINIX= fi - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + fi -else - CC="$ac_cv_prog_CC" -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if ${ac_cv_safe_to_define___extensions__+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + + + +# ----------------------------------------------------------------------------- +# configurable options + +# Check whether --enable-plugin-nfacct was given. +if test "${enable_plugin_nfacct+set}" = set; then : + enableval=$enable_plugin_nfacct; else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + enable_plugin_nfacct="no" + fi +# Check whether --enable-plugin-freeipmi was given. +if test "${enable_plugin_freeipmi+set}" = set; then : + enableval=$enable_plugin_freeipmi; +else + enable_plugin_freeipmi="detect" - fi fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. + +# Check whether --enable-pedantic was given. +if test "${enable_pedantic+set}" = set; then : + enableval=$enable_pedantic; else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS + enable_pedantic="no" -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + + +# Check whether --with-webdir was given. +if test "${with_webdir+set}" = set; then : + withval=$with_webdir; webdir="${withval}" else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + webdir="\$(pkgdatadir)/web" + fi -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 +# Check whether --with-libcap was given. +if test "${with_libcap+set}" = set; then : + withval=$with_libcap; else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS + with_libcap="detect" fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } + + +# Check whether --with-zlib was given. +if test "${with_zlib+set}" = set; then : + withval=$with_zlib; else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + with_zlib="yes" + fi - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 +# Check whether --with-math was given. +if test "${with_math+set}" = set; then : + withval=$with_math; else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. + with_math="yes" + +fi + + +# Check whether --with-user was given. +if test "${with_user+set}" = set; then : + withval=$with_user; else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS + with_user="nobody" fi + +# Check whether --enable-x86-sse was given. +if test "${enable_x86_sse+set}" = set; then : + enableval=$enable_x86_sse; +else + enable_x86_sse="yes" + fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } + +# Check whether --enable-lto was given. +if test "${enable_lto+set}" = set; then : + enableval=$enable_lto; else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + enable_lto="detect" + fi - test -n "$ac_ct_CC" && break -done - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi +# ----------------------------------------------------------------------------- +# netdata required checks -fi +# fails on centos6 +#AX_CHECK_ENABLE_DEBUG() -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((returns_nonnull))" >&5 +$as_echo_n "checking for __attribute__((returns_nonnull))... " >&6; } +if ${ax_cv_have_func_attribute_returns_nonnull+:} false; then : $as_echo_n "(cached) " >&6 else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ + + void *foo( void ) __attribute__((returns_nonnull)); + int main () { -#ifndef __GNUC__ - choke me -#endif ; return 0; } + _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_returns_nonnull=no else - ac_compiler_gnu=no + ax_cv_have_func_attribute_returns_nonnull=yes fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu +else + ax_cv_have_func_attribute_returns_nonnull=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_returns_nonnull" >&5 +$as_echo "$ax_cv_have_func_attribute_returns_nonnull" >&6; } + + if test yes = $ax_cv_have_func_attribute_returns_nonnull; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_RETURNS_NONNULL 1 +_ACEOF + fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((malloc))" >&5 +$as_echo_n "checking for __attribute__((malloc))... " >&6; } +if ${ax_cv_have_func_attribute_malloc+:} false; then : $as_echo_n "(cached) " >&6 else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ + + void *foo( void ) __attribute__((malloc)); + int main () { @@ -5020,30 +5026,49 @@ main () ; return 0; } + _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_malloc=no else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + ax_cv_have_func_attribute_malloc=yes +fi +else + ax_cv_have_func_attribute_malloc=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext -int -main () -{ +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_malloc" >&5 +$as_echo "$ax_cv_have_func_attribute_malloc" >&6; } - ; - return 0; -} + if test yes = $ax_cv_have_func_attribute_malloc; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_MALLOC 1 _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((noreturn))" >&5 +$as_echo_n "checking for __attribute__((noreturn))... " >&6; } +if ${ax_cv_have_func_attribute_noreturn+:} false; then : + $as_echo_n "(cached) " >&6 else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ + + void foo( void ) __attribute__((noreturn)); + int main () { @@ -5051,143 +5076,99 @@ main () ; return 0; } + _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_noreturn=no +else + ax_cv_have_func_attribute_noreturn=yes fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + ax_cv_have_func_attribute_noreturn=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_noreturn" >&5 +$as_echo "$ax_cv_have_func_attribute_noreturn" >&6; } + + if test yes = $ax_cv_have_func_attribute_noreturn; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_NORETURN 1 +_ACEOF + fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((format))" >&5 +$as_echo_n "checking for __attribute__((format))... " >&6; } +if ${ax_cv_have_func_attribute_format+:} false; then : $as_echo_n "(cached) " >&6 else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + int foo(const char *p, ...) __attribute__((format(printf, 1, 2))); -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; int main () { -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; return 0; } + _ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_format=no +else + ax_cv_have_func_attribute_format=yes fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC +else + ax_cv_have_func_attribute_format=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_format" >&5 +$as_echo "$ax_cv_have_func_attribute_format" >&6; } + + if test yes = $ax_cv_have_func_attribute_format; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_FORMAT 1 +_ACEOF fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 -$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } -if ${am_cv_prog_cc_c_o+:} false; then : + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((warn_unused_result))" >&5 +$as_echo_n "checking for __attribute__((warn_unused_result))... " >&6; } +if ${ax_cv_have_func_attribute_warn_unused_result+:} false; then : $as_echo_n "(cached) " >&6 else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ + + int foo( void ) __attribute__((warn_unused_result)); + int main () { @@ -5195,745 +5176,611 @@ main () ; return 0; } + _ACEOF - # Make sure it works both with $CC and with simple cc. - # Following AC_PROG_CC_C_O, we do the test twice because some - # compilers refuse to overwrite an existing .o file with -o, - # though they will create one. - am_cv_prog_cc_c_o=yes - for am_i in 1 2; do - if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 - ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } \ - && test -f conftest2.$ac_objext; then - : OK - else - am_cv_prog_cc_c_o=no - break - fi - done - rm -f core conftest* - unset am_i -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 -$as_echo "$am_cv_prog_cc_c_o" >&6; } -if test "$am_cv_prog_cc_c_o" != yes; then - # Losing compiler, so override with the script. - # FIXME: It is wrong to rewrite CC. - # But if we don't then we get into trouble of one sort or another. - # A longer-term fix would be to have automake use am__CC in this case, - # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" - CC="$am_aux_dir/compile $CC" +if ac_fn_c_try_link "$LINENO"; then : + if test -s conftest.err; then : + ax_cv_have_func_attribute_warn_unused_result=no +else + ax_cv_have_func_attribute_warn_unused_result=yes fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - +else + ax_cv_have_func_attribute_warn_unused_result=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_have_func_attribute_warn_unused_result" >&5 +$as_echo "$ax_cv_have_func_attribute_warn_unused_result" >&6; } + if test yes = $ax_cv_have_func_attribute_warn_unused_result; then : +cat >>confdefs.h <<_ACEOF +#define HAVE_FUNC_ATTRIBUTE_WARN_UNUSED_RESULT 1 +_ACEOF +fi -if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. -set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PKG_CONFIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $PKG_CONFIG in - [\\/]* | ?:[\\/]*) - ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - ;; -esac -fi -PKG_CONFIG=$ac_cv_path_PKG_CONFIG -if test -n "$PKG_CONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 -$as_echo "$PKG_CONFIG" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi + for ac_func in $ac_func_list +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF fi -if test -z "$ac_cv_path_PKG_CONFIG"; then - ac_pt_PKG_CONFIG=$PKG_CONFIG - # Extract the first word of "pkg-config", so it can be a program name with args. -set dummy pkg-config; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $ac_pt_PKG_CONFIG in - [\\/]* | ?:[\\/]*) - ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi done - done -IFS=$as_save_IFS - ;; -esac -fi -ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG -if test -n "$ac_pt_PKG_CONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 -$as_echo "$ac_pt_PKG_CONFIG" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - if test "x$ac_pt_PKG_CONFIG" = x; then - PKG_CONFIG="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - PKG_CONFIG=$ac_pt_PKG_CONFIG - fi -else - PKG_CONFIG="$ac_cv_path_PKG_CONFIG" -fi -fi -if test -n "$PKG_CONFIG"; then - _pkg_min_version=0.9.0 - { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 -$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } - if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - PKG_CONFIG="" - fi -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error + +ac_fn_c_check_type "$LINENO" "struct timespec" "ac_cv_type_struct_timespec" "#include +" +if test "x$ac_cv_type_struct_timespec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_TIMESPEC 1 _ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break fi -rm -f conftest.err conftest.i conftest.$ac_ext +ac_fn_c_check_type "$LINENO" "clockid_t" "ac_cv_type_clockid_t" "#include +" +if test "x$ac_cv_type_clockid_t" = xyes; then : -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi +cat >>confdefs.h <<_ACEOF +#define HAVE_CLOCKID_T 1 +_ACEOF - done - ac_cv_prog_CPP=$CPP fi - CPP=$ac_cv_prog_CPP + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 +$as_echo_n "checking for library containing clock_gettime... " >&6; } +if ${ac_cv_search_clock_gettime+:} false; then : + $as_echo_n "(cached) " >&6 else - ac_cv_prog_CPP=$CPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" #endif - Syntax error +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} _ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : +for ac_lib in '' rt posix4; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_clock_gettime=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_clock_gettime+:} false; then : + break +fi +done +if ${ac_cv_search_clock_gettime+:} false; then : else - # Broken: fails on valid input. -continue + ac_cv_search_clock_gettime=no fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 +$as_echo "$ac_cv_search_clock_gettime" >&6; } +ac_res=$ac_cv_search_clock_gettime +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break fi -rm -f conftest.err conftest.i conftest.$ac_ext +for ac_func in clock_gettime +do : + ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" +if test "x$ac_cv_func_clock_gettime" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CLOCK_GETTIME 1 +_ACEOF + +fi done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } +for ac_func in sched_setscheduler sched_get_priority_min sched_get_priority_max nice +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + fi +done -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu +ac_fn_c_find_intX_t "$LINENO" "8" "ac_cv_c_int8_t" +case $ac_cv_c_int8_t in #( + no|yes) ;; #( + *) -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +cat >>confdefs.h <<_ACEOF +#define int8_t $ac_cv_c_int8_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "16" "ac_cv_c_int16_t" +case $ac_cv_c_int16_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int16_t $ac_cv_c_int16_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" +case $ac_cv_c_int32_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int32_t $ac_cv_c_int32_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" +case $ac_cv_c_int64_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int64_t $ac_cv_c_int64_t +_ACEOF +;; esac - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi +ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" +case $ac_cv_c_uint8_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT8_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint8_t $ac_cv_c_uint8_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" +case $ac_cv_c_uint16_t in #( + no|yes) ;; #( + *) + + +cat >>confdefs.h <<_ACEOF +#define uint16_t $ac_cv_c_uint16_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" +case $ac_cv_c_uint32_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT32_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint32_t $ac_cv_c_uint32_t +_ACEOF +;; + esac -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" +ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" +case $ac_cv_c_uint64_t in #( + no|yes) ;; #( + *) +$as_echo "#define _UINT64_T 1" >>confdefs.h -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : + +cat >>confdefs.h <<_ACEOF +#define uint64_t $ac_cv_c_uint64_t +_ACEOF +;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : $as_echo_n "(cached) " >&6 else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; esac - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi +ac_fn_c_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" +if test "x$ac_cv_have_decl_strerror_r" = xyes; then : + ac_have_decl=1 else - ac_cv_path_EGREP=$EGREP + ac_have_decl=0 fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRERROR_R $ac_have_decl +_ACEOF + +for ac_func in strerror_r +do : + ac_fn_c_check_func "$LINENO" "strerror_r" "ac_cv_func_strerror_r" +if test "x$ac_cv_func_strerror_r" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STRERROR_R 1 +_ACEOF +fi +done -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 +$as_echo_n "checking whether strerror_r returns char *... " >&6; } +if ${ac_cv_func_strerror_r_char_p+:} false; then : $as_echo_n "(cached) " >&6 else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include + ac_cv_func_strerror_r_char_p=no + if test $ac_cv_have_decl_strerror_r = yes; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default int main () { + char buf[100]; + char x = *strerror_r (0, buf, sizeof buf); + char *p = strerror_r (0, buf, sizeof buf); + return !p || x; + ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no + ac_cv_func_strerror_r_char_p=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + else + # strerror_r is not declared. Choose between + # systems that have relatively inaccessible declarations for the + # function. BeOS and DEC UNIX 4.0 fall in this category, but the + # former has a strerror_r that returns char*, while the latter + # has a strerror_r that returns `int'. + # This test should segfault on the DEC system. + if test "$cross_compiling" = yes; then : + : +else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - +$ac_includes_default + extern char *strerror_r (); +int +main () +{ +char buf[100]; + char x = *strerror_r (0, buf, sizeof buf); + return ! isalpha (x); + ; + return 0; +} _ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_strerror_r_char_p=yes fi -rm -f conftest* - +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext fi -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : + fi -else - ac_cv_header_stdc=no fi -rm -f conftest* +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5 +$as_echo "$ac_cv_func_strerror_r_char_p" >&6; } +if test $ac_cv_func_strerror_r_char_p = yes; then + +$as_echo "#define STRERROR_R_CHAR_P 1" >>confdefs.h fi -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Generic" >&5 +$as_echo_n "checking for _Generic... " >&6; } +if ${ac_cv_c__Generic+:} false; then : + $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : + main (int argc, char **argv) + { + int a = _Generic (argc, int: argc = 1); + int *b = &_Generic (argc, default: argc); + char ***c = _Generic (argv, int: argc, default: argv ? &argv : 0); + _Generic (1 ? 0 : b, int: a, default: b) = &argc; + _Generic (a = 1, default: a) = 3; + return a + !b + !c; + } +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c__Generic=yes else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - + ac_cv_c__Generic=no fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c__Generic" >&5 +$as_echo "$ac_cv_c__Generic" >&6; } +if test $ac_cv_c__Generic = yes; then -$as_echo "#define STDC_HEADERS 1" >>confdefs.h +$as_echo "#define HAVE_C__GENERIC 1" >>confdefs.h fi -# On IRIX 5.3, sys/types and inttypes.h are conflicting. -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __atomic" >&5 +$as_echo_n "checking for __atomic... " >&6; } +if ${ac_cv_c___atomic+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int + main (int argc, char **argv) + { + volatile unsigned long ul1 = 1, ul2 = 0, ul3 = 2; + __atomic_compare_exchange(&ul1, &ul2, &ul3, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_fetch_add(&ul1, 1, __ATOMIC_SEQ_CST); + __atomic_fetch_sub(&ul3, 1, __ATOMIC_SEQ_CST); + volatile unsigned long long ull1 = 1, ull2 = 0, ull3 = 2; + __atomic_compare_exchange(&ull1, &ull2, &ull3, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_fetch_add(&ull1, 1, __ATOMIC_SEQ_CST); + __atomic_fetch_sub(&ull3, 1, __ATOMIC_SEQ_CST); + return 0; + } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_c___atomic=yes +else + ac_cv_c___atomic=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c___atomic" >&5 +$as_echo "$ac_cv_c___atomic" >&6; } +if test $ac_cv_c___atomic = yes; then -done +$as_echo "#define HAVE_C___ATOMIC 1" >>confdefs.h +fi +# AC_C_STMT_EXPR +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 +$as_echo_n "checking size of void *... " >&6; } +if ${ac_cv_sizeof_void_p+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then : - ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" -if test "x$ac_cv_header_minix_config_h" = xyes; then : - MINIX=yes else - MINIX= + if test "$ac_cv_type_void_p" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (void *) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_void_p=0 + fi fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 +$as_echo "$ac_cv_sizeof_void_p" >&6; } - if test "$MINIX" = yes; then - -$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h - - -$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h -$as_echo "#define _MINIX 1" >>confdefs.h +cat >>confdefs.h <<_ACEOF +#define SIZEOF_VOID_P $ac_cv_sizeof_void_p +_ACEOF - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 -$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } -if ${ac_cv_safe_to_define___extensions__+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/types.h defines makedev" >&5 +$as_echo_n "checking whether sys/types.h defines makedev... " >&6; } +if ${ac_cv_header_sys_types_h_makedev+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -# define __EXTENSIONS__ 1 - $ac_includes_default +#include int main () { - +return makedev(0, 0); ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_safe_to_define___extensions__=yes +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_header_sys_types_h_makedev=yes else - ac_cv_safe_to_define___extensions__=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_header_sys_types_h_makedev=no fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 -$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } - test $ac_cv_safe_to_define___extensions__ = yes && - $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h - - $as_echo "#define _ALL_SOURCE 1" >>confdefs.h +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext - $as_echo "#define _GNU_SOURCE 1" >>confdefs.h +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_types_h_makedev" >&5 +$as_echo "$ac_cv_header_sys_types_h_makedev" >&6; } - $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h +if test $ac_cv_header_sys_types_h_makedev = no; then +ac_fn_c_check_header_mongrel "$LINENO" "sys/mkdev.h" "ac_cv_header_sys_mkdev_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_mkdev_h" = xyes; then : - $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h +$as_echo "#define MAJOR_IN_MKDEV 1" >>confdefs.h +fi + if test $ac_cv_header_sys_mkdev_h = no; then + ac_fn_c_check_header_mongrel "$LINENO" "sys/sysmacros.h" "ac_cv_header_sys_sysmacros_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_sysmacros_h" = xyes; then : - for ac_func in $ac_func_list -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF +$as_echo "#define MAJOR_IN_SYSMACROS 1" >>confdefs.h fi -done - + fi +fi -ac_fn_c_check_type "$LINENO" "struct timespec" "ac_cv_type_struct_timespec" "#include +for ac_header in sys/types.h netinet/in.h arpa/nameser.h netdb.h resolv.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include /* inet_ functions / structs */ +#endif +#ifdef HAVE_ARPA_NAMESER_H +# include /* DNS HEADER struct */ +#endif +#ifdef HAVE_NETDB_H +# include +#endif " -if test "x$ac_cv_type_struct_timespec" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_STRUCT_TIMESPEC 1 +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF - fi -ac_fn_c_check_type "$LINENO" "clockid_t" "ac_cv_type_clockid_t" "#include -" -if test "x$ac_cv_type_clockid_t" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_CLOCKID_T 1 -_ACEOF +done -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 -$as_echo_n "checking for library containing clock_gettime... " >&6; } -if ${ac_cv_search_clock_gettime+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char clock_gettime (); -int -main () -{ -return clock_gettime (); - ; - return 0; -} -_ACEOF -for ac_lib in '' rt posix4; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_clock_gettime=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_clock_gettime+:} false; then : - break -fi -done -if ${ac_cv_search_clock_gettime+:} false; then : -else - ac_cv_search_clock_gettime=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 -$as_echo "$ac_cv_search_clock_gettime" >&6; } -ac_res=$ac_cv_search_clock_gettime -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" -fi -for ac_func in clock_gettime + for ac_header in $ac_header_list do : - ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" -if test "x$ac_cv_func_clock_gettime" = xyes; then : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF -#define HAVE_CLOCK_GETTIME 1 +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi + done -# Check system type + + + + +# ----------------------------------------------------------------------------- +# operating system detection + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking operating system" >&5 +$as_echo_n "checking operating system... " >&6; } case "$host_os" in freebsd*) - build_target=freebsd - ;; + build_target=freebsd + ;; darwin*) - build_target=macos - LDFLAGS="${LDFLAGS} -framework CoreFoundation -framework IOKit" - ;; + build_target=macos + LDFLAGS="${LDFLAGS} -framework CoreFoundation -framework IOKit" + ;; *) - ;; + build_target=linux + ;; esac - if test x$build_target = xfreebsd; then + if test "${build_target}" = "freebsd"; then FREEBSD_TRUE= FREEBSD_FALSE='#' else @@ -5941,7 +5788,7 @@ else FREEBSD_FALSE= fi - if test x$build_target = xmacos; then + if test "${build_target}" = "macos"; then MACOS_TRUE= MACOS_FALSE='#' else @@ -5949,67 +5796,20 @@ else MACOS_FALSE= fi - -# Check whether --enable-plugin-nfacct was given. -if test "${enable_plugin_nfacct+set}" = set; then : - enableval=$enable_plugin_nfacct; -else - enable_plugin_nfacct="no" - -fi - -# Check whether --enable-pedantic was given. -if test "${enable_pedantic+set}" = set; then : - enableval=$enable_pedantic; -else - enable_pedantic="no" - -fi - - -# Check whether --with-webdir was given. -if test "${with_webdir+set}" = set; then : - withval=$with_webdir; webdir="${withval}" -else - webdir="\$(pkgdatadir)/web" - -fi - - -# Check whether --with-zlib was given. -if test "${with_zlib+set}" = set; then : - withval=$with_zlib; -else - with_zlib="yes" - -fi - - -# Check whether --with-math was given. -if test "${with_math+set}" = set; then : - withval=$with_math; -else - with_math="yes" - -fi - - -# Check whether --with-user was given. -if test "${with_user+set}" = set; then : - withval=$with_user; + if test "${build_target}" = "linux"; then + LINUX_TRUE= + LINUX_FALSE='#' else - with_user="nobody" - + LINUX_TRUE='#' + LINUX_FALSE= fi -# Check whether --enable-x86-sse was given. -if test "${enable_x86_sse+set}" = set; then : - enableval=$enable_x86_sse; -else - enable_x86_sse="yes" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${build_target}" >&5 +$as_echo "${build_target}" >&6; } -fi +# ----------------------------------------------------------------------------- +# pthreads @@ -6182,7 +5982,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ax_pthread_config="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6324,862 +6124,864 @@ $as_echo_n "checking if more special flags are required for pthreads... " >&6; } esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag" >&5 $as_echo "$flag" >&6; } - if test "x$flag" != xno; then - PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 -$as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; } -if ${ax_cv_PTHREAD_PRIO_INHERIT+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -int i = PTHREAD_PRIO_INHERIT; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ax_cv_PTHREAD_PRIO_INHERIT=yes -else - ax_cv_PTHREAD_PRIO_INHERIT=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 -$as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } - if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"; then : - -$as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h - -fi - - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" - - # More AIX lossage: compile with *_r variant - if test "x$GCC" != xyes; then - case $host_os in - aix*) - case "x/$CC" in #( - x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) : - #handle absolute path differently from PATH based program lookup - case "x$CC" in #( - x/*) : - if as_fn_executable_p ${CC}_r; then : - PTHREAD_CC="${CC}_r" -fi ;; #( - *) : - for ac_prog in ${CC}_r -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_PTHREAD_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$PTHREAD_CC"; then - ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_PTHREAD_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -PTHREAD_CC=$ac_cv_prog_PTHREAD_CC -if test -n "$PTHREAD_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 -$as_echo "$PTHREAD_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$PTHREAD_CC" && break -done -test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" - ;; -esac ;; #( - *) : - ;; -esac - ;; - esac - fi -fi - -test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" - - - - - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test x"$ax_pthread_ok" = xyes; then - -$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h - - : -else - ax_pthread_ok=no - as_fn_error $? "Cannot initialize pthread environment" "$LINENO" 5 -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -LIBS="${PTHREAD_LIBS} ${LIBS}" -CFLAGS="${CFLAGS} ${PTHREAD_CFLAGS}" -CC="${PTHREAD_CC}" - - - -# Check whether --with-jemalloc-prefix was given. -if test "${with_jemalloc_prefix+set}" = set; then : - withval=$with_jemalloc_prefix; - jemalloc_prefix="$withval" - -else - - if test "`uname -s`" = "Darwin"; then - jemalloc_prefix="je_" - else - jemalloc_prefix="" - fi - - -fi - - -cat >>confdefs.h <<_ACEOF -#define prefix_jemalloc ${jemalloc_prefix} -_ACEOF - - -enable_jemalloc=no - -# Check whether --with-jemalloc was given. -if test "${with_jemalloc+set}" = set; then : - withval=$with_jemalloc; - if test "$withval" != "no"; then - if test "x${enable_tcmalloc}" = "xyes"; then - as_fn_error $? "Cannot compile with both jemalloc and tcmalloc" "$LINENO" 5 - fi - enable_jemalloc=yes - jemalloc_base_dir="$withval" - case "$withval" in - yes) - jemalloc_base_dir="/usr" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking checking for jemalloc includes standard directories" >&5 -$as_echo_n "checking checking for jemalloc includes standard directories... " >&6; } - ;; - *":"*) - jemalloc_include="`echo $withval |sed -e 's/:.*$//'`" - jemalloc_ldflags="`echo $withval |sed -e 's/^.*://'`" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking checking for jemalloc includes in $jemalloc_include libs in $jemalloc_ldflags" >&5 -$as_echo_n "checking checking for jemalloc includes in $jemalloc_include libs in $jemalloc_ldflags... " >&6; } - ;; - *) - jemalloc_include="$withval/include" - jemalloc_ldflags="$withval/lib" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking checking for jemalloc includes in $withval" >&5 -$as_echo_n "checking checking for jemalloc includes in $withval... " >&6; } - ;; - esac - fi - -fi - + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi -has_jemalloc=0 -if test "$enable_jemalloc" != "no"; then - jemalloc_have_headers=0 - jemalloc_have_libs=0 - if test "$jemalloc_base_dir" != "/usr"; then - CFLAGS="${CFLAGS} -I${jemalloc_include}" - LDFLAGS="${LDFLAGS} -L${jemalloc_ldflags}" - LIBTOOL_LINK_FLAGS="${LIBTOOL_LINK_FLAGS} -R${jemalloc_ldflags}" - fi - func="${jemalloc_prefix}malloc_stats_print" - as_ac_Lib=`$as_echo "ac_cv_lib_jemalloc_${func}" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${func} in -ljemalloc" >&5 -$as_echo_n "checking for ${func} in -ljemalloc... " >&6; } -if eval \${$as_ac_Lib+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 +$as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; } +if ${ax_cv_PTHREAD_PRIO_INHERIT+:} false; then : $as_echo_n "(cached) " >&6 else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ljemalloc $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char ${func} (); + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include int main () { -return ${func} (); +int i = PTHREAD_PRIO_INHERIT; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" + ax_cv_PTHREAD_PRIO_INHERIT=yes else - eval "$as_ac_Lib=no" + ax_cv_PTHREAD_PRIO_INHERIT=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - jemalloc_have_libs=1 -fi - if test "$jemalloc_have_libs" != "0"; then - for ac_header in jemalloc/jemalloc.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "jemalloc/jemalloc.h" "ac_cv_header_jemalloc_jemalloc_h" "$ac_includes_default" -if test "x$ac_cv_header_jemalloc_jemalloc_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_JEMALLOC_JEMALLOC_H 1 -_ACEOF - jemalloc_have_headers=1 fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 +$as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } + if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"; then : -done - - fi - if test "$jemalloc_have_headers" != "0"; then - has_jemalloc=1 - LIBS="${LIBS} -ljemalloc" - -$as_echo "#define has_jemalloc 1" >>confdefs.h +$as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h - else - as_fn_error $? "Couldn't find a jemalloc installation" "$LINENO" 5 - fi fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" -if test "$has_jemalloc" = "1"; then - -$as_echo "#define ENABLE_JEMALLOC 1" >>confdefs.h - + # More AIX lossage: compile with *_r variant + if test "x$GCC" != xyes; then + case $host_os in + aix*) + case "x/$CC" in #( + x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) : + #handle absolute path differently from PATH based program lookup + case "x$CC" in #( + x/*) : + if { test -f ${CC}_r && $as_test_x ${CC}_r; }; then : + PTHREAD_CC="${CC}_r" +fi ;; #( + *) : + for ac_prog in ${CC}_r +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PTHREAD_CC+:} false; then : + $as_echo_n "(cached) " >&6 else - - -# Check whether --with-tcmalloc-lib was given. -if test "${with_tcmalloc_lib+set}" = set; then : - withval=$with_tcmalloc_lib; - with_tcmalloc_lib="$withval" - + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_PTHREAD_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - with_tcmalloc_lib="tcmalloc" +fi +fi +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 +$as_echo "$PTHREAD_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + test -n "$PTHREAD_CC" && break +done +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + ;; +esac ;; #( + *) : + ;; +esac + ;; + esac + fi fi +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" -has_tcmalloc=0 -# Check whether --with-tcmalloc was given. -if test "${with_tcmalloc+set}" = set; then : - withval=$with_tcmalloc; - if test "$withval" != "no"; then - if test "x${enable_jemalloc}" = "xyes"; then - as_fn_error $? "Cannot compile with both tcmalloc and jemalloc" "$LINENO" 5 - fi - tcmalloc_have_lib=0 - if test "x$withval" != "xyes" && test "x$withval" != "x"; then - tcmalloc_ldflags="$withval/lib" - LDFLAGS="${LDFLAGS} -L${tcmalloc_ldflags}" - LIBTOOL_LINK_FLAGS="${LIBTOOL_LINK_FLAGS} -rpath ${tcmalloc_ldflags}" - fi - as_ac_Lib=`$as_echo "ac_cv_lib_${with_tcmalloc_lib}''_tc_cfree" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tc_cfree in -l${with_tcmalloc_lib}" >&5 -$as_echo_n "checking for tc_cfree in -l${with_tcmalloc_lib}... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l${with_tcmalloc_lib} $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char tc_cfree (); -int -main () -{ -return tc_cfree (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - tcmalloc_have_lib=1 -fi - if test "$tcmalloc_have_lib" != "0"; then - LIBS="${LIBS} -l${with_tcmalloc_lib}" - has_tcmalloc=1 -$as_echo "#define has_tcmalloc 1" >>confdefs.h +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then - else - as_fn_error $? "Couldn't find a tcmalloc installation" "$LINENO" 5 - fi - fi +$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h + : +else + ax_pthread_ok=no + as_fn_error $? "Cannot initialize pthread environment" "$LINENO" 5 fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +LIBS="${PTHREAD_LIBS} ${LIBS}" +CFLAGS="${CFLAGS} ${PTHREAD_CFLAGS}" +CC="${PTHREAD_CC}" - if test "$has_tcmalloc" = "1"; then - -$as_echo "#define ENABLE_TCMALLOC 1" >>confdefs.h - - else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mallopt" >&5 -$as_echo_n "checking for mallopt... " >&6; } -if ${ac_cv_c_mallopt+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - int main(int argc, char **argv) { - mallopt(M_ARENA_MAX, 1); - } -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_c_mallopt=yes -else - ac_cv_c_mallopt=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_mallopt" >&5 -$as_echo "$ac_cv_c_mallopt" >&6; } -if test $ac_cv_c_mallopt = yes; then +# ----------------------------------------------------------------------------- +# libm -$as_echo "#define HAVE_C_MALLOPT 1" >>confdefs.h -fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mallinfo" >&5 -$as_echo_n "checking for mallinfo... " >&6; } -if ${ac_cv_c_mallinfo+:} false; then : +if test -z "${MATH_LIBS}"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sin in -lm" >&5 +$as_echo_n "checking for sin in -lm... " >&6; } +if ${ac_cv_lib_m_sin+:} false; then : $as_echo_n "(cached) " >&6 else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char sin (); int main () { - - struct mallinfo mi = mallinfo(); - /* make sure that fields exists */ - mi.uordblks = 0; - mi.hblkhd = 0; - mi.arena = 0; - - +return sin (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - ac_cv_c_mallinfo=yes + ac_cv_lib_m_sin=yes else - ac_cv_c_mallinfo=no + ac_cv_lib_m_sin=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_mallinfo" >&5 -$as_echo "$ac_cv_c_mallinfo" >&6; } -if test $ac_cv_c_mallinfo = yes; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_sin" >&5 +$as_echo "$ac_cv_lib_m_sin" >&6; } +if test "x$ac_cv_lib_m_sin" = xyes; then : + MATH_LIBS="-lm" -$as_echo "#define HAVE_C_MALLINFO 1" >>confdefs.h +fi fi +test "${with_math}" = "yes" -a -z "${MATH_LIBS}" && as_fn_error $? "math required but not found" "$LINENO" 5 - fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if libm should be used" >&5 +$as_echo_n "checking if libm should be used... " >&6; } +if test "${with_math}" != "no" -a ! -z "${MATH_LIBS}"; then + with_math="yes" + +$as_echo "#define STORAGE_WITH_MATH 1" >>confdefs.h + + OPTIONAL_MATH_CFLAGS="${MATH_CFLAGS}" + OPTIONAL_MATH_LIBS="${MATH_LIBS}" +else + with_math="no" fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_math}" >&5 +$as_echo "${with_math}" >&6; } -ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" -case $ac_cv_c_uint8_t in #( - no|yes) ;; #( - *) -$as_echo "#define _UINT8_T 1" >>confdefs.h +# ----------------------------------------------------------------------------- +# zlib -cat >>confdefs.h <<_ACEOF -#define uint8_t $ac_cv_c_uint8_t -_ACEOF -;; - esac +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZLIB" >&5 +$as_echo_n "checking for ZLIB... " >&6; } -ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" -case $ac_cv_c_uint16_t in #( - no|yes) ;; #( - *) +if test -n "$ZLIB_CFLAGS"; then + pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$ZLIB_LIBS"; then + pkg_cv_ZLIB_LIBS="$ZLIB_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi -cat >>confdefs.h <<_ACEOF -#define uint16_t $ac_cv_c_uint16_t -_ACEOF -;; - esac -ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" -case $ac_cv_c_uint32_t in #( - no|yes) ;; #( - *) +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } -$as_echo "#define _UINT32_T 1" >>confdefs.h +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib" 2>&1` + else + ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$ZLIB_PKG_ERRORS" >&5 + have_zlib=no -cat >>confdefs.h <<_ACEOF -#define uint32_t $ac_cv_c_uint32_t -_ACEOF -;; - esac +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_zlib=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 -$as_echo_n "checking for inline... " >&6; } -if ${ac_cv_c_inline+:} false; then : - $as_echo_n "(cached) " >&6 else - ac_cv_c_inline=no -for ac_kw in inline __inline__ __inline; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifndef __cplusplus -typedef int foo_t; -static $ac_kw foo_t static_foo () {return 0; } -$ac_kw foo_t foo () {return 0; } -#endif + ZLIB_CFLAGS=$pkg_cv_ZLIB_CFLAGS + ZLIB_LIBS=$pkg_cv_ZLIB_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_zlib=yes +fi +test "${with_zlib}" = "yes" -a "${have_zlib}" != "yes" && as_fn_error $? "zlib required but not found. Try installing 'zlib1g-dev' or 'zlib-devel'." "$LINENO" 5 -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_inline=$ac_kw +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if zlib should be used" >&5 +$as_echo_n "checking if zlib should be used... " >&6; } +if test "${with_zlib}" != "no" -a "${have_zlib}" = "yes"; then + with_zlib="yes" + +$as_echo "#define NETDATA_WITH_ZLIB 1" >>confdefs.h + + OPTIONAL_ZLIB_CLFAGS="${ZLIB_CFLAGS}" + OPTIONAL_ZLIB_LIBS="${ZLIB_LIBS}" +else + with_zlib="no" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_inline" != no && break -done +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_zlib}" >&5 +$as_echo "${with_zlib}" >&6; } + + +# ----------------------------------------------------------------------------- +# libuuid + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for UUID" >&5 +$as_echo_n "checking for UUID... " >&6; } + +if test -n "$UUID_CFLAGS"; then + pkg_cv_UUID_CFLAGS="$UUID_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"uuid\""; } >&5 + ($PKG_CONFIG --exists --print-errors "uuid") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_UUID_CFLAGS=`$PKG_CONFIG --cflags "uuid" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$UUID_LIBS"; then + pkg_cv_UUID_LIBS="$UUID_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"uuid\""; } >&5 + ($PKG_CONFIG --exists --print-errors "uuid") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_UUID_LIBS=`$PKG_CONFIG --libs "uuid" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 -$as_echo "$ac_cv_c_inline" >&6; } -case $ac_cv_c_inline in - inline | yes) ;; - *) - case $ac_cv_c_inline in - no) ac_val=;; - *) ac_val=$ac_cv_c_inline;; - esac - cat >>confdefs.h <<_ACEOF -#ifndef __cplusplus -#define inline $ac_val -#endif -_ACEOF - ;; -esac -ac_fn_c_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" -if test "x$ac_cv_have_decl_strerror_r" = xyes; then : - ac_have_decl=1 + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes else - ac_have_decl=0 + _pkg_short_errors_supported=no fi + if test $_pkg_short_errors_supported = yes; then + UUID_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "uuid" 2>&1` + else + UUID_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "uuid" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$UUID_PKG_ERRORS" >&5 -cat >>confdefs.h <<_ACEOF -#define HAVE_DECL_STRERROR_R $ac_have_decl -_ACEOF + as_fn_error $? "libuuid required but not found. Try installing 'uuid-dev' or 'libuuid-devel'." "$LINENO" 5 -for ac_func in strerror_r -do : - ac_fn_c_check_func "$LINENO" "strerror_r" "ac_cv_func_strerror_r" -if test "x$ac_cv_func_strerror_r" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STRERROR_R 1 -_ACEOF +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "libuuid required but not found. Try installing 'uuid-dev' or 'libuuid-devel'." "$LINENO" 5 +else + UUID_CFLAGS=$pkg_cv_UUID_CFLAGS + UUID_LIBS=$pkg_cv_UUID_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_uuid=yes fi -done -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 -$as_echo_n "checking whether strerror_r returns char *... " >&6; } -if ${ac_cv_func_strerror_r_char_p+:} false; then : +$as_echo "#define NETDATA_WITH_UUID 1" >>confdefs.h + +OPTIONAL_UUID_CLFAGS="${UUID_CFLAGS}" +OPTIONAL_UUID_LIBS="${UUID_LIBS}" + + +# ----------------------------------------------------------------------------- +# compiler options + + +case $host_cpu in #( + i?86) : + SSE_CANDIDATE="yes" + ;; #( + *) : + ;; +esac + +if test "${SSE_CANDIDATE}" = "yes" -a "${enable_x86_sse}" = "yes"; then + opt="-msse2 -mfpmath=sse" + as_CACHEVAR=`$as_echo "ax_cv_check_cflags__${opt}" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts ${opt}" >&5 +$as_echo_n "checking whether C compiler accepts ${opt}... " >&6; } +if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else - ac_cv_func_strerror_r_char_p=no - if test $ac_cv_have_decl_strerror_r = yes; then - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS ${opt}" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default + int main () { - char buf[100]; - char x = *strerror_r (0, buf, sizeof buf); - char *p = strerror_r (0, buf, sizeof buf); - return !p || x; - ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_func_strerror_r_char_p=yes + eval "$as_CACHEVAR=yes" +else + eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - else - # strerror_r is not declared. Choose between - # systems that have relatively inaccessible declarations for the - # function. BeOS and DEC UNIX 4.0 fall in this category, but the - # former has a strerror_r that returns char*, while the latter - # has a strerror_r that returns `int'. - # This test should segfault on the DEC system. - if test "$cross_compiling" = yes; then : - : + CFLAGS=$ax_check_save_flags +fi +eval ac_res=\$$as_CACHEVAR + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if test x"`eval 'as_val=${'$as_CACHEVAR'};$as_echo "$as_val"'`" = xyes; then : + CFLAGS="${CFLAGS} ${opt}" else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default - extern char *strerror_r (); -int -main () -{ -char buf[100]; - char x = *strerror_r (0, buf, sizeof buf); - return ! isalpha (x); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_func_strerror_r_char_p=yes + : fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext + fi - fi +if test "${GCC}" = "yes"; then -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5 -$as_echo "$ac_cv_func_strerror_r_char_p" >&6; } -if test $ac_cv_func_strerror_r_char_p = yes; then +cat >>confdefs.h <<_ACEOF +#define likely(x) __builtin_expect(!!(x), 1) +_ACEOF -$as_echo "#define STRERROR_R_CHAR_P 1" >>confdefs.h -fi +cat >>confdefs.h <<_ACEOF +#define unlikely(x) __builtin_expect(!!(x), 0) +_ACEOF -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Generic" >&5 -$as_echo_n "checking for _Generic... " >&6; } -if ${ac_cv_c__Generic+:} false; then : - $as_echo_n "(cached) " >&6 else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -int - main (int argc, char **argv) - { - int a = _Generic (argc, int: argc = 1); - int *b = &_Generic (argc, default: argc); - char ***c = _Generic (argv, int: argc, default: argv ? &argv : 0); - _Generic (1 ? 0 : b, int: a, default: b) = &argc; - _Generic (a = 1, default: a) = 3; - return a + !b + !c; - } +cat >>confdefs.h <<_ACEOF +#define likely(x) (x) _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c__Generic=yes -else - ac_cv_c__Generic=no + + +cat >>confdefs.h <<_ACEOF +#define unlikely(x) (x) +_ACEOF + fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test "${enable_pedantic}" = "yes"; then + enable_strict="yes" + CFLAGS="${CFLAGS} -pedantic -Wall -Wextra -Wno-long-long" fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c__Generic" >&5 -$as_echo "$ac_cv_c__Generic" >&6; } -if test $ac_cv_c__Generic = yes; then -$as_echo "#define HAVE_C__GENERIC 1" >>confdefs.h + +# ----------------------------------------------------------------------------- +# memory allocation library + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for memory allocator" >&5 +$as_echo_n "checking for memory allocator... " >&6; } + + +# Check whether --with-jemalloc-prefix was given. +if test "${with_jemalloc_prefix+set}" = set; then : + withval=$with_jemalloc_prefix; + jemalloc_prefix="$withval" + +else + + if test "`uname -s`" = "Darwin"; then + jemalloc_prefix="je_" + else + jemalloc_prefix="" + fi + fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __atomic" >&5 -$as_echo_n "checking for __atomic... " >&6; } -if ${ac_cv_c___atomic+:} false; then : + +cat >>confdefs.h <<_ACEOF +#define prefix_jemalloc ${jemalloc_prefix} +_ACEOF + + +enable_jemalloc=no + +# Check whether --with-jemalloc was given. +if test "${with_jemalloc+set}" = set; then : + withval=$with_jemalloc; + if test "$withval" != "no"; then + if test "x${enable_tcmalloc}" = "xyes"; then + as_fn_error $? "Cannot compile with both jemalloc and tcmalloc" "$LINENO" 5 + fi + enable_jemalloc=yes + jemalloc_base_dir="$withval" + case "$withval" in + yes) + jemalloc_base_dir="/usr" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking checking for jemalloc includes standard directories" >&5 +$as_echo_n "checking checking for jemalloc includes standard directories... " >&6; } + ;; + *":"*) + jemalloc_include="`echo $withval |sed -e 's/:.*$//'`" + jemalloc_ldflags="`echo $withval |sed -e 's/^.*://'`" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking checking for jemalloc includes in $jemalloc_include libs in $jemalloc_ldflags" >&5 +$as_echo_n "checking checking for jemalloc includes in $jemalloc_include libs in $jemalloc_ldflags... " >&6; } + ;; + *) + jemalloc_include="$withval/include" + jemalloc_ldflags="$withval/lib" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking checking for jemalloc includes in $withval" >&5 +$as_echo_n "checking checking for jemalloc includes in $withval... " >&6; } + ;; + esac + fi + +fi + + +has_jemalloc=0 +if test "$enable_jemalloc" != "no"; then + jemalloc_have_headers=0 + jemalloc_have_libs=0 + if test "$jemalloc_base_dir" != "/usr"; then + CFLAGS="${CFLAGS} -I${jemalloc_include}" + LDFLAGS="${LDFLAGS} -L${jemalloc_ldflags}" + LIBTOOL_LINK_FLAGS="${LIBTOOL_LINK_FLAGS} -R${jemalloc_ldflags}" + fi + func="${jemalloc_prefix}malloc_stats_print" + as_ac_Lib=`$as_echo "ac_cv_lib_jemalloc_${func}" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${func} in -ljemalloc" >&5 +$as_echo_n "checking for ${func} in -ljemalloc... " >&6; } +if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljemalloc $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -int - main (int argc, char **argv) - { - volatile unsigned long ul1 = 1, ul2 = 0, ul3 = 2; - __atomic_compare_exchange(&ul1, &ul2, &ul3, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - __atomic_fetch_add(&ul1, 1, __ATOMIC_SEQ_CST); - __atomic_fetch_sub(&ul3, 1, __ATOMIC_SEQ_CST); - volatile unsigned long long ull1 = 1, ull2 = 0, ull3 = 2; - __atomic_compare_exchange(&ull1, &ull2, &ull3, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - __atomic_fetch_add(&ull1, 1, __ATOMIC_SEQ_CST); - __atomic_fetch_sub(&ull3, 1, __ATOMIC_SEQ_CST); - return 0; - } +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ${func} (); +int +main () +{ +return ${func} (); + ; + return 0; +} _ACEOF if ac_fn_c_try_link "$LINENO"; then : - ac_cv_c___atomic=yes + eval "$as_ac_Lib=yes" else - ac_cv_c___atomic=no + eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + jemalloc_have_libs=1 fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c___atomic" >&5 -$as_echo "$ac_cv_c___atomic" >&6; } -if test $ac_cv_c___atomic = yes; then -$as_echo "#define HAVE_C___ATOMIC 1" >>confdefs.h + if test "$jemalloc_have_libs" != "0"; then + for ac_header in jemalloc/jemalloc.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "jemalloc/jemalloc.h" "ac_cv_header_jemalloc_jemalloc_h" "$ac_includes_default" +if test "x$ac_cv_header_jemalloc_jemalloc_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_JEMALLOC_JEMALLOC_H 1 +_ACEOF + jemalloc_have_headers=1 +fi +done + + fi + if test "$jemalloc_have_headers" != "0"; then + has_jemalloc=1 + LIBS="${LIBS} -ljemalloc" + +$as_echo "#define has_jemalloc 1" >>confdefs.h + + else + as_fn_error $? "Couldn't find a jemalloc installation" "$LINENO" 5 + fi fi -# AC_C_STMT_EXPR -# The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 -$as_echo_n "checking size of void *... " >&6; } -if ${ac_cv_sizeof_void_p+:} false; then : - $as_echo_n "(cached) " >&6 + +if test "$has_jemalloc" = "1"; then + +$as_echo "#define ENABLE_JEMALLOC 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: jemalloc" >&5 +$as_echo "jemalloc" >&6; } else - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then : -else - if test "$ac_cv_type_void_p" = yes; then - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (void *) -See \`config.log' for more details" "$LINENO" 5; } - else - ac_cv_sizeof_void_p=0 - fi -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 -$as_echo "$ac_cv_sizeof_void_p" >&6; } +# Check whether --with-tcmalloc-lib was given. +if test "${with_tcmalloc_lib+set}" = set; then : + withval=$with_tcmalloc_lib; + with_tcmalloc_lib="$withval" +else + with_tcmalloc_lib="tcmalloc" -cat >>confdefs.h <<_ACEOF -#define SIZEOF_VOID_P $ac_cv_sizeof_void_p -_ACEOF +fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/types.h defines makedev" >&5 -$as_echo_n "checking whether sys/types.h defines makedev... " >&6; } -if ${ac_cv_header_sys_types_h_makedev+:} false; then : +has_tcmalloc=0 + +# Check whether --with-tcmalloc was given. +if test "${with_tcmalloc+set}" = set; then : + withval=$with_tcmalloc; + if test "$withval" != "no"; then + if test "x${enable_jemalloc}" = "xyes"; then + as_fn_error $? "Cannot compile with both tcmalloc and jemalloc" "$LINENO" 5 + fi + tcmalloc_have_lib=0 + if test "x$withval" != "xyes" && test "x$withval" != "x"; then + tcmalloc_ldflags="$withval/lib" + LDFLAGS="${LDFLAGS} -L${tcmalloc_ldflags}" + LIBTOOL_LINK_FLAGS="${LIBTOOL_LINK_FLAGS} -rpath ${tcmalloc_ldflags}" + fi + as_ac_Lib=`$as_echo "ac_cv_lib_${with_tcmalloc_lib}''_tc_cfree" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tc_cfree in -l${with_tcmalloc_lib}" >&5 +$as_echo_n "checking for tc_cfree in -l${with_tcmalloc_lib}... " >&6; } +if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + ac_check_lib_save_LIBS=$LIBS +LIBS="-l${with_tcmalloc_lib} $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tc_cfree (); int main () { -return makedev(0, 0); +return tc_cfree (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - ac_cv_header_sys_types_h_makedev=yes + eval "$as_ac_Lib=yes" else - ac_cv_header_sys_types_h_makedev=no + eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - +LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_types_h_makedev" >&5 -$as_echo "$ac_cv_header_sys_types_h_makedev" >&6; } - -if test $ac_cv_header_sys_types_h_makedev = no; then -ac_fn_c_check_header_mongrel "$LINENO" "sys/mkdev.h" "ac_cv_header_sys_mkdev_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_mkdev_h" = xyes; then : - -$as_echo "#define MAJOR_IN_MKDEV 1" >>confdefs.h - +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + tcmalloc_have_lib=1 fi + if test "$tcmalloc_have_lib" != "0"; then + LIBS="${LIBS} -l${with_tcmalloc_lib}" + has_tcmalloc=1 +$as_echo "#define has_tcmalloc 1" >>confdefs.h - if test $ac_cv_header_sys_mkdev_h = no; then - ac_fn_c_check_header_mongrel "$LINENO" "sys/sysmacros.h" "ac_cv_header_sys_sysmacros_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_sysmacros_h" = xyes; then : - -$as_echo "#define MAJOR_IN_SYSMACROS 1" >>confdefs.h + else + as_fn_error $? "Couldn't find a tcmalloc installation" "$LINENO" 5 + fi + fi fi - fi -fi - -for ac_header in sys/types.h netinet/in.h arpa/nameser.h netdb.h resolv.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_NETINET_IN_H -# include /* inet_ functions / structs */ -#endif -#ifdef HAVE_ARPA_NAMESER_H -# include /* DNS HEADER struct */ -#endif -#ifdef HAVE_NETDB_H -# include -#endif -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF -fi + if test "$has_tcmalloc" = "1"; then -done +$as_echo "#define ENABLE_TCMALLOC 1" >>confdefs.h + { $as_echo "$as_me:${as_lineno-$LINENO}: result: tcmalloc" >&5 +$as_echo "tcmalloc" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: system" >&5 +$as_echo "system" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mallopt" >&5 +$as_echo_n "checking for mallopt... " >&6; } +if ${ac_cv_c_mallopt+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + int main(int argc, char **argv) { + mallopt(M_ARENA_MAX, 1); + } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_c_mallopt=yes +else + ac_cv_c_mallopt=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_mallopt" >&5 +$as_echo "$ac_cv_c_mallopt" >&6; } +if test $ac_cv_c_mallopt = yes; then +$as_echo "#define HAVE_C_MALLOPT 1" >>confdefs.h -case $host_cpu in #( - i?86) : - SSE_CANDIDATE="yes" - ;; #( - *) : - ;; -esac +fi -if test "${SSE_CANDIDATE}" = "yes" -a "${enable_x86_sse}" = "yes"; then - opt="-msse2 -mfpmath=sse" - as_CACHEVAR=`$as_echo "ax_cv_check_cflags__$opt" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts $opt" >&5 -$as_echo_n "checking whether C compiler accepts $opt... " >&6; } -if eval \${$as_CACHEVAR+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mallinfo" >&5 +$as_echo_n "checking for mallinfo... " >&6; } +if ${ac_cv_c_mallinfo+:} false; then : $as_echo_n "(cached) " >&6 else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS $opt" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - +#include int main () { + struct mallinfo mi = mallinfo(); + /* make sure that fields exists */ + mi.uordblks = 0; + mi.hblkhd = 0; + mi.arena = 0; + + ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$as_CACHEVAR=yes" +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_c_mallinfo=yes else - eval "$as_CACHEVAR=no" + ac_cv_c_mallinfo=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext fi -eval ac_res=\$$as_CACHEVAR - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if test x"`eval 'as_val=${'$as_CACHEVAR'};$as_echo "$as_val"'`" = xyes; then : - CFLAGS="$CFLAGS $opt" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_mallinfo" >&5 +$as_echo "$ac_cv_c_mallinfo" >&6; } +if test $ac_cv_c_mallinfo = yes; then + +$as_echo "#define HAVE_C_MALLINFO 1" >>confdefs.h + +fi + + fi +fi + + +# ----------------------------------------------------------------------------- +# libcap + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBCAP" >&5 +$as_echo_n "checking for LIBCAP... " >&6; } + +if test -n "$LIBCAP_CFLAGS"; then + pkg_cv_LIBCAP_CFLAGS="$LIBCAP_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcap\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libcap") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBCAP_CFLAGS=`$PKG_CONFIG --cflags "libcap" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes else - : + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBCAP_LIBS"; then + pkg_cv_LIBCAP_LIBS="$LIBCAP_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcap\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libcap") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBCAP_LIBS=`$PKG_CONFIG --libs "libcap" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried fi + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no fi + if test $_pkg_short_errors_supported = yes; then + LIBCAP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcap" 2>&1` + else + LIBCAP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcap" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBCAP_PKG_ERRORS" >&5 + have_libcap=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_libcap=no -if test -z "${MATH_LIBS}"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sin in -lm" >&5 -$as_echo_n "checking for sin in -lm... " >&6; } -if ${ac_cv_lib_m_sin+:} false; then : +else + LIBCAP_CFLAGS=$pkg_cv_LIBCAP_CFLAGS + LIBCAP_LIBS=$pkg_cv_LIBCAP_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_get_proc, cap_set_proc in -lcap" >&5 +$as_echo_n "checking for cap_get_proc, cap_set_proc in -lcap... " >&6; } +if ${ac_cv_lib_cap_cap_get_proc__cap_set_proc+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS -LIBS="-lm $LIBS" +LIBS="-lcap $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -7189,48 +6991,108 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char sin (); +char cap_get_proc, cap_set_proc (); int main () { -return sin (); +return cap_get_proc, cap_set_proc (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_m_sin=yes + ac_cv_lib_cap_cap_get_proc__cap_set_proc=yes else - ac_cv_lib_m_sin=no + ac_cv_lib_cap_cap_get_proc__cap_set_proc=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_sin" >&5 -$as_echo "$ac_cv_lib_m_sin" >&6; } -if test "x$ac_cv_lib_m_sin" = xyes; then : - MATH_LIBS="-lm" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cap_cap_get_proc__cap_set_proc" >&5 +$as_echo "$ac_cv_lib_cap_cap_get_proc__cap_set_proc" >&6; } +if test "x$ac_cv_lib_cap_cap_get_proc__cap_set_proc" = xyes; then : + ac_fn_c_check_header_mongrel "$LINENO" "sys/capability.h" "ac_cv_header_sys_capability_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_capability_h" = xyes; then : + have_libcap=yes +else + have_libcap=no + +fi + + +else + have_libcap=no + +fi + +fi +test "${with_libcap}" = "yes" -a "${have_libcap}" != "yes" && as_fn_error $? "libcap required but not found." "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if libcap should be used" >&5 +$as_echo_n "checking if libcap should be used... " >&6; } +if test "${with_libcap}" != "no" -a "${have_libcap}" = "yes"; then + with_libcap="yes" + +$as_echo "#define HAVE_CAPABILITY 1" >>confdefs.h + + OPTIONAL_LIBCAP_CLFAGS="${LIBCAP_CFLAGS}" + OPTIONAL_LIBCAP_LIBS="${LIBCAP_LIBS}" +else + with_libcap="no" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_libcap}" >&5 +$as_echo "${with_libcap}" >&6; } + if test "${with_libcap}" = "yes"; then + ENABLE_CAPABILITY_TRUE= + ENABLE_CAPABILITY_FALSE='#' +else + ENABLE_CAPABILITY_TRUE='#' + ENABLE_CAPABILITY_FALSE= fi + + +# ----------------------------------------------------------------------------- +# apps.plugin + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if apps.plugin should be enabled" >&5 +$as_echo_n "checking if apps.plugin should be enabled... " >&6; } +if test "${build_target}" != "macos"; then + enable_plugin_apps="yes" +else + enable_plugin_apps="no" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${enable_plugin_apps}" >&5 +$as_echo "${enable_plugin_apps}" >&6; } + if test "${enable_plugin_apps}" = "yes"; then + ENABLE_PLUGIN_APPS_TRUE= + ENABLE_PLUGIN_APPS_FALSE='#' +else + ENABLE_PLUGIN_APPS_TRUE='#' + ENABLE_PLUGIN_APPS_FALSE= fi + +# ----------------------------------------------------------------------------- +# freeipmi.plugin - libipmimonitoring + + pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for UUID" >&5 -$as_echo_n "checking for UUID... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for IPMIMONITORING" >&5 +$as_echo_n "checking for IPMIMONITORING... " >&6; } -if test -n "$UUID_CFLAGS"; then - pkg_cv_UUID_CFLAGS="$UUID_CFLAGS" +if test -n "$IPMIMONITORING_CFLAGS"; then + pkg_cv_IPMIMONITORING_CFLAGS="$IPMIMONITORING_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"uuid\""; } >&5 - ($PKG_CONFIG --exists --print-errors "uuid") 2>&5 + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libipmimonitoring\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libipmimonitoring") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_UUID_CFLAGS=`$PKG_CONFIG --cflags "uuid" 2>/dev/null` + pkg_cv_IPMIMONITORING_CFLAGS=`$PKG_CONFIG --cflags "libipmimonitoring" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -7238,16 +7100,16 @@ fi else pkg_failed=untried fi -if test -n "$UUID_LIBS"; then - pkg_cv_UUID_LIBS="$UUID_LIBS" +if test -n "$IPMIMONITORING_LIBS"; then + pkg_cv_IPMIMONITORING_LIBS="$IPMIMONITORING_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"uuid\""; } >&5 - ($PKG_CONFIG --exists --print-errors "uuid") 2>&5 + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libipmimonitoring\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libipmimonitoring") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_UUID_LIBS=`$PKG_CONFIG --libs "uuid" 2>/dev/null` + pkg_cv_IPMIMONITORING_LIBS=`$PKG_CONFIG --libs "libipmimonitoring" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -7268,53 +7130,61 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - UUID_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "uuid" 2>&1` + IPMIMONITORING_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libipmimonitoring" 2>&1` else - UUID_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "uuid" 2>&1` + IPMIMONITORING_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libipmimonitoring" 2>&1` fi # Put the nasty error message in config.log where it belongs - echo "$UUID_PKG_ERRORS" >&5 - - as_fn_error $? "Package requirements (uuid) were not met: + echo "$IPMIMONITORING_PKG_ERRORS" >&5 -$UUID_PKG_ERRORS + have_ipmimonitoring=no -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -Alternatively, you may set the environment variables UUID_CFLAGS -and UUID_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -Alternatively, you may set the environment variables UUID_CFLAGS -and UUID_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. + have_ipmimonitoring=no -To get pkg-config, see . -See \`config.log' for more details" "$LINENO" 5; } else - UUID_CFLAGS=$pkg_cv_UUID_CFLAGS - UUID_LIBS=$pkg_cv_UUID_LIBS + IPMIMONITORING_CFLAGS=$pkg_cv_IPMIMONITORING_CFLAGS + IPMIMONITORING_LIBS=$pkg_cv_IPMIMONITORING_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + have_ipmimonitoring=yes +fi +test "${enable_plugin_freeipmi}" = "yes" -a "${have_ipmimonitoring}" != "yes" && \ + as_fn_error $? "ipmimonitoring required but not found. Try installing 'libipmimonitoring-dev' or 'libipmimonitoring-devel'" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if freeipmi.plugin should be enabled" >&5 +$as_echo_n "checking if freeipmi.plugin should be enabled... " >&6; } +if test "${enable_plugin_freeipmi}" != "no" -a "${have_ipmimonitoring}" = "yes"; then + enable_plugin_freeipmi="yes" + +$as_echo "#define HAVE_FREEIPMI 1" >>confdefs.h + OPTIONAL_IPMIMONITORING_CLFAGS="${IPMIMONITORING_CFLAGS}" + OPTIONAL_IPMIMONITORING_LIBS="${IPMIMONITORING_LIBS}" +else + enable_plugin_freeipmi="no" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${enable_plugin_freeipmi}" >&5 +$as_echo "${enable_plugin_freeipmi}" >&6; } + if test "${enable_plugin_freeipmi}" = "yes"; then + ENABLE_PLUGIN_FREEIPMI_TRUE= + ENABLE_PLUGIN_FREEIPMI_FALSE='#' +else + ENABLE_PLUGIN_FREEIPMI_TRUE='#' + ENABLE_PLUGIN_FREEIPMI_FALSE= fi -test -z "${UUID_LIBS}" && as_fn_error $? "libuuid required but not found. Try installing 'uuid-dev' or 'libuuid-devel'." "$LINENO" 5 -$as_echo "#define NETDATA_WITH_UUID 1" >>confdefs.h -OPTIONAL_UUID_CLFAGS="${UUID_CFLAGS}" -OPTIONAL_UUID_LIBS="${UUID_LIBS}" -if test "${enable_plugin_nfacct}" = "yes"; then +# ----------------------------------------------------------------------------- +# nfacct.plugin - libmnl, libnetfilter_acct + + + + + pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for NFACCT" >&5 @@ -7374,39 +7244,22 @@ fi # Put the nasty error message in config.log where it belongs echo "$NFACCT_PKG_ERRORS" >&5 - as_fn_error $? "Package requirements (libnetfilter_acct) were not met: - -$NFACCT_PKG_ERRORS + have_libnetfilter_acct=no -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -Alternatively, you may set the environment variables NFACCT_CFLAGS -and NFACCT_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -Alternatively, you may set the environment variables NFACCT_CFLAGS -and NFACCT_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. + have_libnetfilter_acct=no -To get pkg-config, see . -See \`config.log' for more details" "$LINENO" 5; } else NFACCT_CFLAGS=$pkg_cv_NFACCT_CFLAGS NFACCT_LIBS=$pkg_cv_NFACCT_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - + have_libnetfilter_acct=yes fi + pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBMNL" >&5 $as_echo_n "checking for LIBMNL... " >&6; } @@ -7465,182 +7318,157 @@ fi # Put the nasty error message in config.log where it belongs echo "$LIBMNL_PKG_ERRORS" >&5 - as_fn_error $? "Package requirements (libmnl) were not met: - -$LIBMNL_PKG_ERRORS + have_libmnl=no -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -Alternatively, you may set the environment variables LIBMNL_CFLAGS -and LIBMNL_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -Alternatively, you may set the environment variables LIBMNL_CFLAGS -and LIBMNL_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. + have_libmnl=no -To get pkg-config, see . -See \`config.log' for more details" "$LINENO" 5; } else LIBMNL_CFLAGS=$pkg_cv_LIBMNL_CFLAGS LIBMNL_LIBS=$pkg_cv_LIBMNL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - + have_libmnl=yes fi - test -z "${NFACCT_LIBS}" && as_fn_error $? "netfilter_acct required but not found" "$LINENO" 5 - test -z "${LIBMNL_LIBS}" && as_fn_error $? "libmnl required but not found. Try installing 'libmnl-dev' or 'libmnl-devel'" "$LINENO" 5 -$as_echo "#define INTERNAL_PLUGIN_NFACCT 1" >>confdefs.h +test "${enable_plugin_nfacct}" = "yes" -a "${have_libnetfilter_acct}" != "yes" && \ + as_fn_error $? "netfilter_acct required but not found" "$LINENO" 5 - OPTIONAL_NFACCT_CLFAGS="${NFACCT_CFLAGS} ${LIBMNL_CFLAGS}" - OPTIONAL_NFACCT_LIBS="${NFACCT_LIBS} ${LIBMNL_LIBS}" -fi -if test "${with_zlib}" = "yes"; then +test "${enable_plugin_nfacct}" = "yes" -a "${have_libmnl}" != "yes" && \ + as_fn_error $? "libmnl required but not found. Try installing 'libmnl-dev' or 'libmnl-devel'" "$LINENO" 5 -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZLIB" >&5 -$as_echo_n "checking for ZLIB... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if nfacct.plugin should be enabled" >&5 +$as_echo_n "checking if nfacct.plugin should be enabled... " >&6; } +if test "${enable_plugin_nfacct}" != "no" -a "${have_libnetfilter_acct}" = "yes" -a "${have_libmnl}" = "yes"; then + enable_plugin_nfacct="yes" -if test -n "$ZLIB_CFLAGS"; then - pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 - ($PKG_CONFIG --exists --print-errors "zlib") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$ZLIB_LIBS"; then - pkg_cv_ZLIB_LIBS="$ZLIB_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 - ($PKG_CONFIG --exists --print-errors "zlib") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi +$as_echo "#define HAVE_LIBMNL 1" >>confdefs.h +$as_echo "#define HAVE_LIBNETFILTER_ACCT 1" >>confdefs.h -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes +$as_echo "#define INTERNAL_PLUGIN_NFACCT 1" >>confdefs.h + + OPTIONAL_NFACCT_CLFAGS="${NFACCT_CFLAGS} ${LIBMNL_CFLAGS}" + OPTIONAL_NFACCT_LIBS="${NFACCT_LIBS} ${LIBMNL_LIBS}" else - _pkg_short_errors_supported=no + enable_plugin_nfacct="no" fi - if test $_pkg_short_errors_supported = yes; then - ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib" 2>&1` - else - ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$ZLIB_PKG_ERRORS" >&5 - - as_fn_error $? "Package requirements (zlib) were not met: - -$ZLIB_PKG_ERRORS - -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -Alternatively, you may set the environment variables ZLIB_CFLAGS -and ZLIB_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details." "$LINENO" 5 -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -Alternatively, you may set the environment variables ZLIB_CFLAGS -and ZLIB_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. - -To get pkg-config, see . -See \`config.log' for more details" "$LINENO" 5; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${enable_plugin_nfacct}" >&5 +$as_echo "${enable_plugin_nfacct}" >&6; } + if test "${enable_plugin_nfacct}" = "yes"; then + ENABLE_PLUGIN_NFACCT_TRUE= + ENABLE_PLUGIN_NFACCT_FALSE='#' else - ZLIB_CFLAGS=$pkg_cv_ZLIB_CFLAGS - ZLIB_LIBS=$pkg_cv_ZLIB_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - + ENABLE_PLUGIN_NFACCT_TRUE='#' + ENABLE_PLUGIN_NFACCT_FALSE= fi - test -z "${ZLIB_LIBS}" && as_fn_error $? "zlib required but not found. Try installing 'zlib1g-dev' or 'zlib-devel'." "$LINENO" 5 - -$as_echo "#define NETDATA_WITH_ZLIB 1" >>confdefs.h - OPTIONAL_ZLIB_CLFAGS="${ZLIB_CFLAGS}" - OPTIONAL_ZLIB_LIBS="${ZLIB_LIBS}" -fi -if test "${with_math}" = "yes"; then - test -z "${MATH_LIBS}" && as_fn_error $? "math required but not found" "$LINENO" 5 -$as_echo "#define STORAGE_WITH_MATH 1" >>confdefs.h - OPTIONAL_MATH_CFLAGS="${MATH_CFLAGS}" - OPTIONAL_MATH_LIBS="${MATH_LIBS}" -fi +# ----------------------------------------------------------------------------- +# Link-Time-Optimization -if test "${GCC}" = "yes"; then +if test "${enable_lto}" != "no"; then + opt="-flto" + as_CACHEVAR=`$as_echo "ax_cv_check_cflags__${opt}" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts ${opt}" >&5 +$as_echo_n "checking whether C compiler accepts ${opt}... " >&6; } +if eval \${$as_CACHEVAR+:} false; then : + $as_echo_n "(cached) " >&6 +else -cat >>confdefs.h <<_ACEOF -#define likely(x) __builtin_expect(!!(x), 1) -_ACEOF + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS ${opt}" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int +main () +{ -cat >>confdefs.h <<_ACEOF -#define unlikely(x) __builtin_expect(!!(x), 0) + ; + return 0; +} _ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$as_CACHEVAR=yes" +else + eval "$as_CACHEVAR=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +eval ac_res=\$$as_CACHEVAR + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if test x"`eval 'as_val=${'$as_CACHEVAR'};$as_echo "$as_val"'`" = xyes; then : + have_lto=yes +else + have_lto=no +fi +fi +if test "${have_lto}" = "yes"; then + oCFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} -flto ${OPTIONAL_MATH_CLFAGS} ${OPTIONAL_NFACCT_CLFAGS} ${OPTIONAL_ZLIB_CLFAGS} ${OPTIONAL_UUID_CLFAGS} ${OPTIONAL_LIBCAP_CFLAGS} ${OPTIONAL_IPMIMONITORING_CFLAGS}" + ac_cv_c_lto_cross_compile="${enable_lto}" + test "${ac_cv_c_lto_cross_compile}" != "yes" && ac_cv_c_lto_cross_compile="no" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if -flto builds executables" >&5 +$as_echo_n "checking if -flto builds executables... " >&6; } +if ${ac_cv_c_lto+:} false; then : + $as_echo_n "(cached) " >&6 else + if test "$cross_compiling" = yes; then : + ac_cv_c_lto=${ac_cv_c_lto_cross_compile} +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + int main(int argc, char **argv) { + return 0; + } -cat >>confdefs.h <<_ACEOF -#define likely(x) (x) _ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_lto=yes +else + ac_cv_c_lto=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_lto" >&5 +$as_echo "$ac_cv_c_lto" >&6; } +if test "${ac_cv_c_lto}" = "yes"; then -cat >>confdefs.h <<_ACEOF -#define unlikely(x) (x) -_ACEOF +$as_echo "#define HAVE_LTO 1" >>confdefs.h fi -if test "${enable_pedantic}" = "yes"; then - enable_strict="yes" - CFLAGS="${CFLAGS} -pedantic -Wall -Wextra -Wno-long-long" + CFLAGS="${oCFLAGS}" + test "${ac_cv_c_lto}" != "yes" && have_lto="no" +fi +test "${enable_lto}" = "yes" -a "${have_lto}" != "yes" && \ + as_fn_error $? "LTO is required but is not available." "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if LTO should be enabled" >&5 +$as_echo_n "checking if LTO should be enabled... " >&6; } +if test "${enable_lto}" != "no" -a "${have_lto}" = "yes"; then + enable_lto="yes" + CFLAGS="${CFLAGS} -flto" +else + enable_lto="no" fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${enable_lto}" >&5 +$as_echo "${enable_lto}" >&6; } + + +# ----------------------------------------------------------------------------- cat >>confdefs.h <<_ACEOF @@ -7677,6 +7505,10 @@ pluginsdir="\$(libexecdir)/netdata/plugins.d" + + + + ac_config_files="$ac_config_files Makefile charts.d/Makefile conf.d/Makefile netdata.spec python.d/Makefile node.d/Makefile plugins.d/Makefile src/Makefile system/Makefile web/Makefile contrib/Makefile" cat >confcache <<\_ACEOF @@ -7791,22 +7623,6 @@ LTLIBOBJS=$ac_ltlibobjs if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 -$as_echo_n "checking that generated files are newer than configure... " >&6; } - if test -n "$am_sleep_pid"; then - # Hide warnings about reused PIDs. - wait $am_sleep_pid 2>/dev/null - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 -$as_echo "done" >&6; } -if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then - as_fn_error $? "conditional \"AMDEP\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then - as_fn_error $? "conditional \"am__fastdepCC\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -n "$EXEEXT"; then am__EXEEXT_TRUE= @@ -7816,6 +7632,14 @@ else am__EXEEXT_FALSE= fi +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${FREEBSD_TRUE}" && test -z "${FREEBSD_FALSE}"; then as_fn_error $? "conditional \"FREEBSD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -7824,6 +7648,26 @@ if test -z "${MACOS_TRUE}" && test -z "${MACOS_FALSE}"; then as_fn_error $? "conditional \"MACOS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${LINUX_TRUE}" && test -z "${LINUX_FALSE}"; then + as_fn_error $? "conditional \"LINUX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_CAPABILITY_TRUE}" && test -z "${ENABLE_CAPABILITY_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_CAPABILITY\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_PLUGIN_APPS_TRUE}" && test -z "${ENABLE_PLUGIN_APPS_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_PLUGIN_APPS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_PLUGIN_FREEIPMI_TRUE}" && test -z "${ENABLE_PLUGIN_FREEIPMI_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_PLUGIN_FREEIPMI\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_PLUGIN_NFACCT_TRUE}" && test -z "${ENABLE_PLUGIN_NFACCT_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_PLUGIN_NFACCT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 @@ -8122,16 +7966,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' + as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -8191,16 +8035,28 @@ else as_mkdir_p=false fi - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -8221,8 +8077,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by netdata $as_me 1.5.0, which was -generated by GNU Autoconf 2.69. Invocation command line was +This file was extended by netdata $as_me 1.6.0, which was +generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -8287,11 +8143,11 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -netdata config.status 1.5.0 -configured by $0, generated by GNU Autoconf 2.69, +netdata config.status 1.6.0 +configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2010 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -8382,7 +8238,7 @@ fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' @@ -9025,7 +8881,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { - # Older Autoconf quotes --file arguments for eval, but not when files + # Autoconf 2.62 quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in @@ -9038,7 +8894,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but + # We used to match only the files named `Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. @@ -9072,19 +8928,21 @@ $as_echo X"$mf" | continue fi # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. + # from the Makefile without running `make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue + test -z "am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || @@ -9158,5 +9016,5 @@ fi test "${with_math}" != "yes" && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You are building without math. math allows accurate calculations. It should be enabled." >&5 $as_echo "$as_me: WARNING: You are building without math. math allows accurate calculations. It should be enabled." >&2;} || : -test "${with_zlib}" != "yes" && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You are building without zlib. zlib allows netdata to trasnfer a lot less data with web clients. It should be enabled." >&5 -$as_echo "$as_me: WARNING: You are building without zlib. zlib allows netdata to trasnfer a lot less data with web clients. It should be enabled." >&2;} || : +test "${with_zlib}" != "yes" && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You are building without zlib. zlib allows netdata to transfer a lot less data with web clients. It should be enabled." >&5 +$as_echo "$as_me: WARNING: You are building without zlib. zlib allows netdata to transfer a lot less data with web clients. It should be enabled." >&2;} || : diff --git a/configure.ac b/configure.ac index d0a6ebd6f..2c83830ca 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_PREREQ(2.60) define([VERSION_MAJOR], [1]) -define([VERSION_MINOR], [5]) +define([VERSION_MINOR], [6]) define([VERSION_FIX], [0]) define([VERSION_NUMBER], VERSION_MAJOR[.]VERSION_MINOR[.]VERSION_FIX) define([VERSION_SUFFIX], []) @@ -12,7 +12,11 @@ define([VERSION_SUFFIX], []) dnl Set to "1" for a first RPM release of a new version PACKAGE_RPM_RELEASE="1" -AC_INIT([netdata], VERSION_NUMBER[]VERSION_SUFFIX) +# We do not use m4_esyscmd_s to support older autoconf. +define([VERSION_STRING], m4_esyscmd(git describe 2>/dev/null | sed 's/^v//' | tr -d '\n')) +m4_ifval(VERSION_STRING, [], [define([VERSION_STRING], VERSION_NUMBER)]) + +AC_INIT([netdata], VERSION_STRING[]VERSION_SUFFIX) AM_MAINTAINER_MODE([disable]) if test x"$USE_MAINTAINER_MODE" = xyes; then @@ -24,14 +28,9 @@ PACKAGE_RPM_VERSION="VERSION_NUMBER" AC_SUBST([PACKAGE_RPM_VERSION]) AC_SUBST([PACKAGE_RPM_RELEASE]) -# fails on centos6 -#AX_CHECK_ENABLE_DEBUG() -AX_GCC_FUNC_ATTRIBUTE(returns_nonnull) -AX_GCC_FUNC_ATTRIBUTE(malloc) -AX_GCC_FUNC_ATTRIBUTE(noreturn) -AX_GCC_FUNC_ATTRIBUTE(format) -AX_GCC_FUNC_ATTRIBUTE(warn_unused_result) +# ----------------------------------------------------------------------------- +# autoconf initialization AC_CONFIG_AUX_DIR([.]) AC_CONFIG_HEADERS([config.h]) @@ -43,91 +42,99 @@ AC_PROG_CC AC_PROG_INSTALL PKG_PROG_PKG_CONFIG AC_USE_SYSTEM_EXTENSIONS -AC_CHECK_FUNCS_ONCE(accept4) -AC_CHECK_TYPES([struct timespec, clockid_t], [], [], [[#include ]]) -AC_SEARCH_LIBS([clock_gettime], [rt posix4]) -AC_CHECK_FUNCS([clock_gettime]) -# Check system type -case "$host_os" in -freebsd*) - build_target=freebsd - ;; -darwin*) - build_target=macos - LDFLAGS="${LDFLAGS} -framework CoreFoundation -framework IOKit" - ;; -*) - ;; -esac -AM_CONDITIONAL([FREEBSD], [test x$build_target = xfreebsd]) -AM_CONDITIONAL([MACOS], [test x$build_target = xmacos]) +# ----------------------------------------------------------------------------- +# configurable options AC_ARG_ENABLE( - [plugin-nfacct], - [AS_HELP_STRING([--enable-plugin-nfacct], [enable nfacct plugin, requires root])], - , - [enable_plugin_nfacct="no"] + [plugin-nfacct], + [AS_HELP_STRING([--enable-plugin-nfacct], [enable nfacct plugin, requires root])], + , + [enable_plugin_nfacct="no"] +) +AC_ARG_ENABLE( + [plugin-freeipmi], + [AS_HELP_STRING([--enable-plugin-freeipmi], [enable freeipmi plugin])], + , + [enable_plugin_freeipmi="detect"] ) AC_ARG_ENABLE( - [pedantic], - [AS_HELP_STRING([--enable-pedantic], [enable pedantic compiler warnings])], - , - [enable_pedantic="no"] + [pedantic], + [AS_HELP_STRING([--enable-pedantic], [enable pedantic compiler warnings])], + , + [enable_pedantic="no"] +) +AC_ARG_WITH( + [webdir], + [AS_HELP_STRING([--with-webdir], [location of webdir @<:@PKGDATADIR/web@:>@])], + [webdir="${withval}"], + [webdir="\$(pkgdatadir)/web"] ) AC_ARG_WITH( - [webdir], - [AS_HELP_STRING([--with-webdir], [location of webdir @<:@PKGDATADIR/web@:>@])], - [webdir="${withval}"], - [webdir="\$(pkgdatadir)/web"] + [libcap], + [AS_HELP_STRING([--with-libcap], [build with libcap])], + , + [with_libcap="detect"] ) AC_ARG_WITH( - [zlib], - [AS_HELP_STRING([--with-zlib], [build with zlib])], - , - [with_zlib="yes"] + [zlib], + [AS_HELP_STRING([--with-zlib], [build with zlib])], + , + [with_zlib="yes"] ) AC_ARG_WITH( - [math], - [AS_HELP_STRING([--with-math], [build with math])], - , - [with_math="yes"] + [math], + [AS_HELP_STRING([--with-math], [build with math])], + , + [with_math="yes"] ) AC_ARG_WITH( - [user], - [AS_HELP_STRING([--with-user], [use this user to drop privilege])], - , - [with_user="nobody"] + [user], + [AS_HELP_STRING([--with-user], [use this user to drop privilege])], + , + [with_user="nobody"] ) AC_ARG_ENABLE( - [x86-sse], - [AS_HELP_STRING([--disable-x86-sse], [SSE/SS2 optimizations on x86 @<:@default enabled@:>@])], - , - [enable_x86_sse="yes"] + [x86-sse], + [AS_HELP_STRING([--disable-x86-sse], [SSE/SS2 optimizations on x86 @<:@default enabled@:>@])], + , + [enable_x86_sse="yes"] +) +AC_ARG_ENABLE( + [lto], + [AS_HELP_STRING([--disable-lto], [Link Time Optimizations @<:@default enabled@:>@])], + , + [enable_lto="detect"] ) -ACX_PTHREAD(, [AC_MSG_ERROR([Cannot initialize pthread environment])]) -LIBS="${PTHREAD_LIBS} ${LIBS}" -CFLAGS="${CFLAGS} ${PTHREAD_CFLAGS}" -CC="${PTHREAD_CC}" -TS_CHECK_JEMALLOC -if test "$has_jemalloc" = "1"; then - AC_DEFINE([ENABLE_JEMALLOC], [1], [compile and link with jemalloc]) -else - TS_CHECK_TCMALLOC - if test "$has_tcmalloc" = "1"; then - AC_DEFINE([ENABLE_TCMALLOC], [1], [compile and link with tcmalloc]) - else - AC_C_MALLOPT - AC_C_MALLINFO - fi -fi +# ----------------------------------------------------------------------------- +# netdata required checks + +# fails on centos6 +#AX_CHECK_ENABLE_DEBUG() +AX_GCC_FUNC_ATTRIBUTE([returns_nonnull]) +AX_GCC_FUNC_ATTRIBUTE([malloc]) +AX_GCC_FUNC_ATTRIBUTE([noreturn]) +AX_GCC_FUNC_ATTRIBUTE([format]) +AX_GCC_FUNC_ATTRIBUTE([warn_unused_result]) + +AC_CHECK_FUNCS_ONCE(accept4) +AC_CHECK_TYPES([struct timespec, clockid_t], [], [], [[#include ]]) +AC_SEARCH_LIBS([clock_gettime], [rt posix4]) +AC_CHECK_FUNCS([clock_gettime]) +AC_CHECK_FUNCS([sched_setscheduler sched_get_priority_min sched_get_priority_max nice]) + +AC_TYPE_INT8_T +AC_TYPE_INT16_T +AC_TYPE_INT32_T +AC_TYPE_INT64_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T +AC_TYPE_UINT64_T AC_C_INLINE AC_FUNC_STRERROR_R AC_C__GENERIC @@ -138,79 +145,290 @@ AC_CANONICAL_HOST AC_HEADER_MAJOR AC_HEADER_RESOLV +AC_CHECK_HEADERS_ONCE([sys/prctl.h]) + + +# ----------------------------------------------------------------------------- +# operating system detection + +AC_MSG_CHECKING([operating system]) +case "$host_os" in +freebsd*) + build_target=freebsd + ;; +darwin*) + build_target=macos + LDFLAGS="${LDFLAGS} -framework CoreFoundation -framework IOKit" + ;; +*) + build_target=linux + ;; +esac + +AM_CONDITIONAL([FREEBSD], [test "${build_target}" = "freebsd"]) +AM_CONDITIONAL([MACOS], [test "${build_target}" = "macos"]) +AM_CONDITIONAL([LINUX], [test "${build_target}" = "linux"]) +AC_MSG_RESULT([${build_target}]) + + +# ----------------------------------------------------------------------------- +# pthreads + +ACX_PTHREAD(, [AC_MSG_ERROR([Cannot initialize pthread environment])]) +LIBS="${PTHREAD_LIBS} ${LIBS}" +CFLAGS="${CFLAGS} ${PTHREAD_CFLAGS}" +CC="${PTHREAD_CC}" + + +# ----------------------------------------------------------------------------- +# libm + +AC_ARG_VAR([MATH_CFLAGS], [C compiler flags for math]) +AC_ARG_VAR([MATH_LIBS], [linker flags for math]) +if test -z "${MATH_LIBS}"; then + AC_CHECK_LIB( + [m], + [sin], + [MATH_LIBS="-lm"] + ) +fi +test "${with_math}" = "yes" -a -z "${MATH_LIBS}" && AC_MSG_ERROR([math required but not found]) + +AC_MSG_CHECKING([if libm should be used]) +if test "${with_math}" != "no" -a ! -z "${MATH_LIBS}"; then + with_math="yes" + AC_DEFINE([STORAGE_WITH_MATH], [1], [math usability]) + OPTIONAL_MATH_CFLAGS="${MATH_CFLAGS}" + OPTIONAL_MATH_LIBS="${MATH_LIBS}" +else + with_math="no" +fi +AC_MSG_RESULT([${with_math}]) + + +# ----------------------------------------------------------------------------- +# zlib + +PKG_CHECK_MODULES( + [ZLIB], + [zlib], + [have_zlib=yes], + [have_zlib=no] +) +test "${with_zlib}" = "yes" -a "${have_zlib}" != "yes" && AC_MSG_ERROR([zlib required but not found. Try installing 'zlib1g-dev' or 'zlib-devel'.]) + +AC_MSG_CHECKING([if zlib should be used]) +if test "${with_zlib}" != "no" -a "${have_zlib}" = "yes"; then + with_zlib="yes" + AC_DEFINE([NETDATA_WITH_ZLIB], [1], [zlib usability]) + OPTIONAL_ZLIB_CLFAGS="${ZLIB_CFLAGS}" + OPTIONAL_ZLIB_LIBS="${ZLIB_LIBS}" +else + with_zlib="no" +fi +AC_MSG_RESULT([${with_zlib}]) + + +# ----------------------------------------------------------------------------- +# libuuid + +PKG_CHECK_MODULES( + [UUID], + [uuid], + [have_uuid=yes], + [AC_MSG_ERROR([libuuid required but not found. Try installing 'uuid-dev' or 'libuuid-devel'.])] +) +AC_DEFINE([NETDATA_WITH_UUID], [1], [uuid usability]) +OPTIONAL_UUID_CLFAGS="${UUID_CFLAGS}" +OPTIONAL_UUID_LIBS="${UUID_LIBS}" + + +# ----------------------------------------------------------------------------- +# compiler options + AC_ARG_VAR([SSE_CANDIDATE], [C compiler flags for SSE]) AS_CASE([$host_cpu], - [i?86], [SSE_CANDIDATE="yes"] + [i?86], [SSE_CANDIDATE="yes"] ) AC_SUBST([SSE_CANDIDATE]) if test "${SSE_CANDIDATE}" = "yes" -a "${enable_x86_sse}" = "yes"; then - opt="-msse2 -mfpmath=sse" - AX_CHECK_COMPILE_FLAG($opt, [CFLAGS="$CFLAGS $opt"], []) + opt="-msse2 -mfpmath=sse" + AX_CHECK_COMPILE_FLAG(${opt}, [CFLAGS="${CFLAGS} ${opt}"], []) fi -AC_ARG_VAR([MATH_CFLAGS], [C compiler flags for math]) -AC_ARG_VAR([MATH_LIBS], [linker flags for math]) -if test -z "${MATH_LIBS}"; then - AC_CHECK_LIB( - [m], - [sin], - [MATH_LIBS="-lm"] - ) +if test "${GCC}" = "yes"; then + AC_DEFINE_UNQUOTED([likely(x)], [__builtin_expect(!!(x), 1)], [gcc branch optimization]) + AC_DEFINE_UNQUOTED([unlikely(x)], [__builtin_expect(!!(x), 0)], [gcc branch optimization]) +else + AC_DEFINE_UNQUOTED([likely(x)], [(x)], [gcc branch optimization]) + AC_DEFINE_UNQUOTED([unlikely(x)], [(x)], [gcc branch optimization]) +fi + +if test "${enable_pedantic}" = "yes"; then + enable_strict="yes" + CFLAGS="${CFLAGS} -pedantic -Wall -Wextra -Wno-long-long" +fi + + +# ----------------------------------------------------------------------------- +# memory allocation library + +AC_MSG_CHECKING([for memory allocator]) +TS_CHECK_JEMALLOC +if test "$has_jemalloc" = "1"; then + AC_DEFINE([ENABLE_JEMALLOC], [1], [compile and link with jemalloc]) + AC_MSG_RESULT([jemalloc]) +else + TS_CHECK_TCMALLOC + if test "$has_tcmalloc" = "1"; then + AC_DEFINE([ENABLE_TCMALLOC], [1], [compile and link with tcmalloc]) + AC_MSG_RESULT([tcmalloc]) + else + AC_MSG_RESULT([system]) + AC_C_MALLOPT + AC_C_MALLINFO + fi fi + +# ----------------------------------------------------------------------------- +# libcap + PKG_CHECK_MODULES( - [UUID], - [uuid], + [LIBCAP], + [libcap], + [AC_CHECK_LIB([cap], [cap_get_proc, cap_set_proc], + [AC_CHECK_HEADER( + [sys/capability.h], + [have_libcap=yes], + [have_libcap=no] + )], + [have_libcap=no] + )], + [have_libcap=no] ) -test -z "${UUID_LIBS}" && AC_MSG_ERROR([libuuid required but not found. Try installing 'uuid-dev' or 'libuuid-devel'.]) -AC_DEFINE([NETDATA_WITH_UUID], [1], [uuid settings]) -OPTIONAL_UUID_CLFAGS="${UUID_CFLAGS}" -OPTIONAL_UUID_LIBS="${UUID_LIBS}" +test "${with_libcap}" = "yes" -a "${have_libcap}" != "yes" && AC_MSG_ERROR([libcap required but not found.]) -if test "${enable_plugin_nfacct}" = "yes"; then - PKG_CHECK_MODULES( - [NFACCT], - [libnetfilter_acct], - ) - PKG_CHECK_MODULES( - [LIBMNL], - [libmnl], - ) - test -z "${NFACCT_LIBS}" && AC_MSG_ERROR([netfilter_acct required but not found]) - test -z "${LIBMNL_LIBS}" && AC_MSG_ERROR([libmnl required but not found. Try installing 'libmnl-dev' or 'libmnl-devel']) - AC_DEFINE([INTERNAL_PLUGIN_NFACCT], [1], [nfacct plugin settings]) - OPTIONAL_NFACCT_CLFAGS="${NFACCT_CFLAGS} ${LIBMNL_CFLAGS}" - OPTIONAL_NFACCT_LIBS="${NFACCT_LIBS} ${LIBMNL_LIBS}" +AC_MSG_CHECKING([if libcap should be used]) +if test "${with_libcap}" != "no" -a "${have_libcap}" = "yes"; then + with_libcap="yes" + AC_DEFINE([HAVE_CAPABILITY], [1], [libcap usability]) + OPTIONAL_LIBCAP_CLFAGS="${LIBCAP_CFLAGS}" + OPTIONAL_LIBCAP_LIBS="${LIBCAP_LIBS}" +else + with_libcap="no" fi -if test "${with_zlib}" = "yes"; then - PKG_CHECK_MODULES( - [ZLIB], - [zlib], - ) - test -z "${ZLIB_LIBS}" && AC_MSG_ERROR([zlib required but not found. Try installing 'zlib1g-dev' or 'zlib-devel'.]) - AC_DEFINE([NETDATA_WITH_ZLIB], [1], [zlib settings]) - OPTIONAL_ZLIB_CLFAGS="${ZLIB_CFLAGS}" - OPTIONAL_ZLIB_LIBS="${ZLIB_LIBS}" +AC_MSG_RESULT([${with_libcap}]) +AM_CONDITIONAL([ENABLE_CAPABILITY], [test "${with_libcap}" = "yes"]) + + +# ----------------------------------------------------------------------------- +# apps.plugin + +AC_MSG_CHECKING([if apps.plugin should be enabled]) +if test "${build_target}" != "macos"; then + enable_plugin_apps="yes" +else + enable_plugin_apps="no" fi -if test "${with_math}" = "yes"; then - test -z "${MATH_LIBS}" && AC_MSG_ERROR([math required but not found]) - AC_DEFINE([STORAGE_WITH_MATH], [1], [math settings]) - OPTIONAL_MATH_CFLAGS="${MATH_CFLAGS}" - OPTIONAL_MATH_LIBS="${MATH_LIBS}" +AC_MSG_RESULT([${enable_plugin_apps}]) +AM_CONDITIONAL([ENABLE_PLUGIN_APPS], [test "${enable_plugin_apps}" = "yes"]) + + +# ----------------------------------------------------------------------------- +# freeipmi.plugin - libipmimonitoring + +PKG_CHECK_MODULES( + [IPMIMONITORING], + [libipmimonitoring], + [have_ipmimonitoring=yes], + [have_ipmimonitoring=no] +) +test "${enable_plugin_freeipmi}" = "yes" -a "${have_ipmimonitoring}" != "yes" && \ + AC_MSG_ERROR([ipmimonitoring required but not found. Try installing 'libipmimonitoring-dev' or 'libipmimonitoring-devel']) + +AC_MSG_CHECKING([if freeipmi.plugin should be enabled]) +if test "${enable_plugin_freeipmi}" != "no" -a "${have_ipmimonitoring}" = "yes"; then + enable_plugin_freeipmi="yes" + AC_DEFINE([HAVE_FREEIPMI], [1], [ipmimonitoring usability]) + OPTIONAL_IPMIMONITORING_CLFAGS="${IPMIMONITORING_CFLAGS}" + OPTIONAL_IPMIMONITORING_LIBS="${IPMIMONITORING_LIBS}" +else + enable_plugin_freeipmi="no" fi +AC_MSG_RESULT([${enable_plugin_freeipmi}]) +AM_CONDITIONAL([ENABLE_PLUGIN_FREEIPMI], [test "${enable_plugin_freeipmi}" = "yes"]) -if test "${GCC}" = "yes"; then - AC_DEFINE_UNQUOTED([likely(x)], [__builtin_expect(!!(x), 1)], [gcc branch optimization]) - AC_DEFINE_UNQUOTED([unlikely(x)], [__builtin_expect(!!(x), 0)], [gcc branch optimization]) + +# ----------------------------------------------------------------------------- +# nfacct.plugin - libmnl, libnetfilter_acct + +AC_CHECK_HEADERS_ONCE([linux/netfilter/nfnetlink_conntrack.h]) + +PKG_CHECK_MODULES( + [NFACCT], + [libnetfilter_acct], + [have_libnetfilter_acct=yes], + [have_libnetfilter_acct=no] +) + +PKG_CHECK_MODULES( + [LIBMNL], + [libmnl], + [have_libmnl=yes], + [have_libmnl=no] +) + +test "${enable_plugin_nfacct}" = "yes" -a "${have_libnetfilter_acct}" != "yes" && \ + AC_MSG_ERROR([netfilter_acct required but not found]) + +test "${enable_plugin_nfacct}" = "yes" -a "${have_libmnl}" != "yes" && \ + AC_MSG_ERROR([libmnl required but not found. Try installing 'libmnl-dev' or 'libmnl-devel']) + +AC_MSG_CHECKING([if nfacct.plugin should be enabled]) +if test "${enable_plugin_nfacct}" != "no" -a "${have_libnetfilter_acct}" = "yes" -a "${have_libmnl}" = "yes"; then + enable_plugin_nfacct="yes" + AC_DEFINE([HAVE_LIBMNL], [1], [libmnl usability]) + AC_DEFINE([HAVE_LIBNETFILTER_ACCT], [1], [libnetfilter_acct usability]) + AC_DEFINE([INTERNAL_PLUGIN_NFACCT], [1], [nfacct plugin usability]) + OPTIONAL_NFACCT_CLFAGS="${NFACCT_CFLAGS} ${LIBMNL_CFLAGS}" + OPTIONAL_NFACCT_LIBS="${NFACCT_LIBS} ${LIBMNL_LIBS}" else - AC_DEFINE_UNQUOTED([likely(x)], [(x)], [gcc branch optimization]) - AC_DEFINE_UNQUOTED([unlikely(x)], [(x)], [gcc branch optimization]) + enable_plugin_nfacct="no" fi +AC_MSG_RESULT([${enable_plugin_nfacct}]) +AM_CONDITIONAL([ENABLE_PLUGIN_NFACCT], [test "${enable_plugin_nfacct}" = "yes"]) -if test "${enable_pedantic}" = "yes"; then - enable_strict="yes" - CFLAGS="${CFLAGS} -pedantic -Wall -Wextra -Wno-long-long" + +# ----------------------------------------------------------------------------- +# Link-Time-Optimization + +if test "${enable_lto}" != "no"; then + opt="-flto" + AX_CHECK_COMPILE_FLAG(${opt}, [have_lto=yes], [have_lto=no]) +fi +if test "${have_lto}" = "yes"; then + oCFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} -flto ${OPTIONAL_MATH_CLFAGS} ${OPTIONAL_NFACCT_CLFAGS} ${OPTIONAL_ZLIB_CLFAGS} ${OPTIONAL_UUID_CLFAGS} ${OPTIONAL_LIBCAP_CFLAGS} ${OPTIONAL_IPMIMONITORING_CFLAGS}" + ac_cv_c_lto_cross_compile="${enable_lto}" + test "${ac_cv_c_lto_cross_compile}" != "yes" && ac_cv_c_lto_cross_compile="no" + AC_C_LTO + CFLAGS="${oCFLAGS}" + test "${ac_cv_c_lto}" != "yes" && have_lto="no" fi +test "${enable_lto}" = "yes" -a "${have_lto}" != "yes" && \ + AC_MSG_ERROR([LTO is required but is not available.]) +AC_MSG_CHECKING([if LTO should be enabled]) +if test "${enable_lto}" != "no" -a "${have_lto}" = "yes"; then + enable_lto="yes" + CFLAGS="${CFLAGS} -flto" +else + enable_lto="no" +fi +AC_MSG_RESULT([${enable_lto}]) + + +# ----------------------------------------------------------------------------- AC_DEFINE_UNQUOTED([NETDATA_USER], ["${with_user}"], [use this user to drop privileged]) @@ -233,21 +451,25 @@ AC_SUBST([OPTIONAL_ZLIB_CLFAGS]) AC_SUBST([OPTIONAL_ZLIB_LIBS]) AC_SUBST([OPTIONAL_UUID_CLFAGS]) AC_SUBST([OPTIONAL_UUID_LIBS]) +AC_SUBST([OPTIONAL_LIBCAP_CFLAGS]) +AC_SUBST([OPTIONAL_LIBCAP_LIBS]) +AC_SUBST([OPTIONAL_IPMIMONITORING_CFLAGS]) +AC_SUBST([OPTIONAL_IPMIMONITORING_LIBS]) AC_CONFIG_FILES([ - Makefile - charts.d/Makefile - conf.d/Makefile - netdata.spec - python.d/Makefile - node.d/Makefile - plugins.d/Makefile - src/Makefile - system/Makefile - web/Makefile - contrib/Makefile + Makefile + charts.d/Makefile + conf.d/Makefile + netdata.spec + python.d/Makefile + node.d/Makefile + plugins.d/Makefile + src/Makefile + system/Makefile + web/Makefile + contrib/Makefile ]) AC_OUTPUT test "${with_math}" != "yes" && AC_MSG_WARN([You are building without math. math allows accurate calculations. It should be enabled.]) || : -test "${with_zlib}" != "yes" && AC_MSG_WARN([You are building without zlib. zlib allows netdata to trasnfer a lot less data with web clients. It should be enabled.]) || : +test "${with_zlib}" != "yes" && AC_MSG_WARN([You are building without zlib. zlib allows netdata to transfer a lot less data with web clients. It should be enabled.]) || : diff --git a/contrib/Makefile.in b/contrib/Makefile.in index eed37f4d9..29db296aa 100644 --- a/contrib/Makefile.in +++ b/contrib/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,9 +36,11 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = contrib +DIST_COMMON = $(dist_noinst_DATA) $(dist_noinst_SCRIPTS) \ + $(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 \ @@ -101,39 +49,17 @@ 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_noinst_SCRIPTS) \ - $(dist_noinst_DATA) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = SCRIPTS = $(dist_noinst_SCRIPTS) -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 = SOURCES = DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac DATA = $(dist_noinst_DATA) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -157,7 +83,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@ @@ -171,6 +101,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@ @@ -306,6 +240,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu contrib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu contrib/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -323,11 +258,11 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -tags TAGS: - -ctags CTAGS: +tags: TAGS +TAGS: -cscope cscopelist: +ctags: CTAGS +CTAGS: distdir: $(DISTFILES) @@ -463,18 +398,15 @@ uninstall-am: .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ - ctags-am distclean distclean-generic distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am 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-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ - pdf-am ps ps-am tags-am uninstall uninstall-am - -.PRECIOUS: Makefile +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am 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-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am debian/changelog: diff --git a/contrib/debian/changelog b/contrib/debian/changelog deleted file mode 100644 index bc8f48cd5..000000000 --- a/contrib/debian/changelog +++ /dev/null @@ -1,3 +0,0 @@ -netdata (1.5.0) UNRELEASED; urgency=medium - * Latest release - -- Netdata Team <> Mon, 23 Jan 2017 21:57:01 +0200 diff --git a/contrib/debian/compat b/contrib/debian/compat deleted file mode 100644 index ec635144f..000000000 --- a/contrib/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/contrib/debian/control b/contrib/debian/control deleted file mode 100644 index 24c5f4c4b..000000000 --- a/contrib/debian/control +++ /dev/null @@ -1,25 +0,0 @@ -Source: netdata -Build-Depends: debhelper (>= 9), - dh-autoreconf, - dh-systemd (>= 1.5), - dpkg-dev (>= 1.13.19), - zlib1g-dev, - uuid-dev -Section: net -Priority: optional -Maintainer: Costa Tsaousis -Standards-Version: 3.9.6 -Homepage: https://github.com/firehol/netdata/wiki - -Package: netdata -Architecture: any -Depends: adduser, - libcap2-bin (>= 1:2.0), - lsb-base (>= 3.1-23.2), - ${misc:Depends}, - ${shlibs:Depends} -Description: real-time charts for system monitoring - Netdata is a daemon that collects data in realtime (per second) - and presents a web site to view and analyze them. The presentation - is also real-time and full of interactive charts that precisely - render all collected values. diff --git a/contrib/debian/control.wheezy b/contrib/debian/control.wheezy deleted file mode 100644 index 4103908a2..000000000 --- a/contrib/debian/control.wheezy +++ /dev/null @@ -1,25 +0,0 @@ -Source: netdata -Build-Depends: debhelper (>= 9), - dh-autoreconf, - pkg-config, - dpkg-dev (>= 1.13.19), - zlib1g-dev, - uuid-dev -Section: net -Priority: optional -Maintainer: Costa Tsaousis -Standards-Version: 3.9.6 -Homepage: https://github.com/firehol/netdata/wiki - -Package: netdata -Architecture: any -Depends: adduser, - libcap2-bin (>= 1:2.0), - lsb-base (>= 3.1-23.2), - ${misc:Depends}, - ${shlibs:Depends} -Description: real-time charts for system monitoring - Netdata is a daemon that collects data in realtime (per second) - and presents a web site to view and analyze them. The presentation - is also real-time and full of interactive charts that precisely - render all collected values. diff --git a/contrib/debian/copyright b/contrib/debian/copyright deleted file mode 100644 index 11a3d6390..000000000 --- a/contrib/debian/copyright +++ /dev/null @@ -1,10 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: Netdata -Upstream-Contact: Costa Tsaousis -Source: https://github.com/firehol/netdata - -Files: * -Copyright: 2014-2016, Costa Tsaousis -License: GPL-3+ - On Debian systems, the complete text of the GNU General Public - License version 3 can be found in /usr/share/common-licenses/GPL-3. diff --git a/contrib/debian/netdata.conf b/contrib/debian/netdata.conf deleted file mode 100644 index a963d80b7..000000000 --- a/contrib/debian/netdata.conf +++ /dev/null @@ -1,16 +0,0 @@ -# NetData Configuration - -# The current full configuration can be retrieved from the running -# server at the URL -# -# http://localhost:19999/netdata.conf -# -# for example: -# -# wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf -# - -[global] - run as user = netdata - web files owner = root - web files group = netdata diff --git a/contrib/debian/netdata.default b/contrib/debian/netdata.default deleted file mode 100644 index 9e7f8ae6e..000000000 --- a/contrib/debian/netdata.default +++ /dev/null @@ -1,5 +0,0 @@ -# Extra arguments to pass to netdata -# -#EXTRA_OPTS="" -#uncomment following line if you are building a wheezy-package -#EXTRA_OPTS="-P /var/run/netdata.pid" diff --git a/contrib/debian/netdata.docs b/contrib/debian/netdata.docs deleted file mode 100644 index 56631abf1..000000000 --- a/contrib/debian/netdata.docs +++ /dev/null @@ -1 +0,0 @@ -ChangeLog diff --git a/contrib/debian/netdata.init b/contrib/debian/netdata.init deleted file mode 100755 index c1b2b74de..000000000 --- a/contrib/debian/netdata.init +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh -# Start/stop the netdata daemon. -# -### BEGIN INIT INFO -# Provides: netdata -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Should-Start: $network -# Should-Stop: $network -# Default-Start: 2 3 4 5 -# Default-Stop: -# Short-Description: Real-time charts for system monitoring -# Description: Netdata is a daemon that collects data in realtime (per second) -# and presents a web site to view and analyze them. The presentation -# is also real-time and full of interactive charts that precisely -# render all collected values. -### END INIT INFO - -PATH=/bin:/usr/bin:/sbin:/usr/sbin -DESC="netdata daemon" -NAME=netdata -DAEMON=/usr/sbin/netdata -PIDFILE=/var/run/netdata/netdata.pid -SCRIPTNAME=/etc/init.d/"$NAME" - -test -f $DAEMON || exit 0 - -. /lib/lsb/init-functions - -[ -r /etc/default/netdata ] && . /etc/default/netdata - -case "$1" in -start) log_daemon_msg "Starting real-time system monitoring" "netdata" - start_daemon -p $PIDFILE $DAEMON $EXTRA_OPTS - log_end_msg $? - ;; -stop) log_daemon_msg "Stopping real-time system monitoring" "netdata" - killproc -p $PIDFILE $DAEMON - RETVAL=$? - [ $RETVAL -eq 0 ] && [ -e "$PIDFILE" ] && rm -f $PIDFILE - log_end_msg $RETVAL - # wait for plugins to exit - sleep 1 - ;; -restart|force-reload) log_daemon_msg "Restarting real-time system monitoring" "netdata" - $0 stop - $0 start - ;; -status) - status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $? - ;; -*) log_action_msg "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" - exit 2 - ;; -esac -exit 0 diff --git a/contrib/debian/netdata.install b/contrib/debian/netdata.install deleted file mode 100644 index 45d42b635..000000000 --- a/contrib/debian/netdata.install +++ /dev/null @@ -1 +0,0 @@ -debian/netdata.conf /etc/netdata/ diff --git a/contrib/debian/netdata.lintian-overrides b/contrib/debian/netdata.lintian-overrides deleted file mode 100644 index 45b2d868f..000000000 --- a/contrib/debian/netdata.lintian-overrides +++ /dev/null @@ -1,16 +0,0 @@ - -# See Debian policy 10.9. apps.plugin has extra capabilities, so don't let -# normal users run it. -netdata: non-standard-executable-perm usr/lib/*/netdata/plugins.d/apps.plugin 0754 != 0755 - - -# FontAwesome is at least in the fonts-font-awesome package, but this is -# not available in wheezy. glyphicons-halflings-regular isn't currently in -# a Debian package. Therefore don't complain about shipping them with netdata -# for the time being. -netdata: duplicate-font-file usr/share/netdata/fonts/* -netdata: font-in-non-font-package usr/share/netdata/fonts/* - -# Files here are marked as conffiles so that local updates to the html files -# isn't clobbered on upgrade. -netdata: non-etc-file-marked-as-conffile var/lib/netdata/www/* diff --git a/contrib/debian/netdata.postinst.in b/contrib/debian/netdata.postinst.in deleted file mode 100644 index 29615f541..000000000 --- a/contrib/debian/netdata.postinst.in +++ /dev/null @@ -1,41 +0,0 @@ -#! /bin/sh - -set -e - -case "$1" in - configure) - if [ -z "$2" ]; then - if ! getent group netdata >/dev/null; then - addgroup --quiet --system netdata - fi - - if ! getent passwd netdata >/dev/null; then - adduser --quiet --system --ingroup netdata --home /var/lib/netdata --no-create-home netdata - fi - - if ! dpkg-statoverride --list /var/lib/netdata >/dev/null 2>&1; then - dpkg-statoverride --update --add netdata netdata 0755 /var/lib/netdata - fi - - if ! dpkg-statoverride --list /var/lib/netdata/www >/dev/null 2>&1; then - dpkg-statoverride --update --add root netdata 0755 /var/lib/netdata/www - fi - - if ! dpkg-statoverride --list /var/cache/netdata >/dev/null 2>&1; then - dpkg-statoverride --update --add netdata netdata 0755 /var/cache/netdata - fi - - fi - - dpkg-statoverride --update --add --force root netdata 0775 /var/lib/netdata/registry - chown -R root:netdata /usr/share/netdata/* - chown -R root:netdata /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d - setcap cap_dac_read_search,cap_sys_ptrace+ep /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d/apps.plugin - -#PERMS# - ;; -esac - -#DEBHELPER# - -exit 0 diff --git a/contrib/debian/netdata.postrm b/contrib/debian/netdata.postrm deleted file mode 100644 index 4ab4eeadd..000000000 --- a/contrib/debian/netdata.postrm +++ /dev/null @@ -1,43 +0,0 @@ -#! /bin/sh - -set -e - -case "$1" in - remove) - ;; - - purge) - if dpkg-statoverride --list | grep -qw /var/cache/netdata; then - dpkg-statoverride --remove /var/cache/netdata - fi - - if dpkg-statoverride --list | grep -qw /var/lib/netdata/www; then - dpkg-statoverride --remove /var/lib/netdata/www - fi - - if dpkg-statoverride --list | grep -qw /var/lib/netdata; then - dpkg-statoverride --remove /var/lib/netdata - fi - - if getent passwd netdata >/dev/null; then - if [ -x /usr/sbin/deluser ]; then - deluser --quiet --system netdata || echo "Unable to remove netdata user" - fi - fi - - if getent group netdata >/dev/null; then - if [ -x /usr/sbin/delgroup ]; then - delgroup --quiet --system netdata || echo "Unable to remove netdata group" - fi - fi - - ;; - - *) - ;; -esac - -#DEBHELPER# - -exit 0 - diff --git a/contrib/debian/netdata.service b/contrib/debian/netdata.service deleted file mode 100644 index e5d3a3863..000000000 --- a/contrib/debian/netdata.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=netdata real-time system monitoring -After=network.target - -[Service] -Type=simple -EnvironmentFile=-/etc/default/netdata -ExecStart=/usr/sbin/netdata -D $EXTRA_OPTS -TimeoutStopSec=30 -Restart=always -RestartSec=5 - -[Install] -WantedBy=multi-user.target diff --git a/contrib/debian/rules b/contrib/debian/rules deleted file mode 100755 index ec4ec4182..000000000 --- a/contrib/debian/rules +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/make -f - -# Find the arch we are building for, as this determines -# the location of plugins in /usr/lib -DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) -TOP = $(CURDIR)/debian/netdata - -%: - # For jessie and beyond - # - dh $@ --with autoreconf,systemd - - # For wheezy or other non-systemd distributions use the following. You - # should also see contrib/README.md which gives details of updates to - # make to debian/control. - # - #dh $@ --with autoreconf - -override_dh_auto_configure: - dh_auto_configure -- --with-math --with-webdir=/var/lib/netdata/www - -debian/%.postinst: debian/%.postinst.in - sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' $< > $@ - -override_dh_install: debian/netdata.postinst - dh_install - - # Remove unneeded .keep files - # - find "$(TOP)" -name .keep -exec rm '{}' ';' - - # Move files that local user shouldn't be editing to /usr/share/netdata - # - mkdir -p "$(TOP)/usr/share/netdata" - for D in $$(find "$(TOP)/var/lib/netdata/www/" -maxdepth 1 -type d -printf '%f '); do \ - echo Relocating $$D; \ - mv "$(TOP)/var/lib/netdata/www/$$D" "$(TOP)/usr/share/netdata/$$D"; \ - ln -s "/usr/share/netdata/$$D" "$(TOP)/var/lib/netdata/www/$$D"; \ - done - - # Update postinst to set correct group for www files on installation. - # Should probably be dpkg-statoverride really, but that gets *really* - # messy. We also set all web files in /var as conffiles so an upgrade - # doesn't splat them. - # - for D in $$(find "$(TOP)/var/lib/netdata/www/" -maxdepth 1 -type f -printf '%f '); do \ - echo Updating postinst for $$D; \ - sed -i "s/^#PERMS#/chgrp netdata \/var\/lib\/netdata\/www\/$$D\n#PERMS#/g" \ - $(CURDIR)/debian/netdata.postinst; \ - echo "/var/lib/netdata/www/$$D" >> $(CURDIR)/debian/netdata.conffiles; \ - done - sed -i "/^#PERMS#/d" $(CURDIR)/debian/netdata.postinst - -override_dh_installdocs: - dh_installdocs - - # Docs should not be under /usr/lib - # - mv $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/plugins.d/README.md \ - $(TOP)/usr/share/doc/netdata/README.plugins.md - mv $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/charts.d/README.md \ - $(TOP)/usr/share/doc/netdata/README.charts.md - - # This doc is currently empty, so no point installing it. - # - rm $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/node.d/README.md - -override_dh_fixperms: - dh_fixperms - - # apps.plugin should only be runnable by the netdata user. It will be - # given extra capabilities in the postinst script. - # - chmod 0754 $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/plugins.d/apps.plugin - -override_dh_installlogrotate: - cp system/netdata.logrotate debian/netdata.logrotate - dh_installlogrotate - -override_dh_clean: - dh_clean - - # Tidy up copied/generated files - # - -[ -r $(CURDIR)/debian/netdata.logrotate ] && rm $(CURDIR)/debian/netdata.logrotate - -[ -r $(CURDIR)/debian/netdata.postinst ] && rm $(CURDIR)/debian/netdata.postinst - -[ -r $(CURDIR)/debian/netdata.conffiles ] && rm $(CURDIR)/debian/netdata.conffiles diff --git a/contrib/debian/source/format b/contrib/debian/source/format deleted file mode 100644 index 89ae9db8f..000000000 --- a/contrib/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (native) diff --git a/coverity-scan.sh b/coverity-scan.sh index a690fead7..8ea433453 100755 --- a/coverity-scan.sh +++ b/coverity-scan.sh @@ -18,6 +18,9 @@ covbuild="$(which cov-build 2>/dev/null || command -v cov-build 2>/dev/null)" echo "The command ${covbuild} is not executable. Save command the full filename of cov-build in .coverity-build" && \ exit 1 +version="$(cat config.h | grep "^#define PACKAGE_VERSION" | cut -d '"' -f 2)" +echo >&2 "Working on netdata version: ${version}" + echo >&2 "Cleaning up old builds..." make clean || exit 1 @@ -34,6 +37,6 @@ tar czvf netdata-coverity-analysis.tgz cov-int || exit 1 curl --form token="${token}" \ --form email=costa@tsaousis.gr \ --form file=@netdata-coverity-analysis.tgz \ - --form version="1.5.0rc1" \ - --form description="Description" \ + --form version="${version}" \ + --form description="netdata, real-time performance monitoring, done right." \ https://scan.coverity.com/builds?project=firehol%2Fnetdata diff --git a/depcomp b/depcomp index fc98710e2..bd0ac0895 100755 --- a/depcomp +++ b/depcomp @@ -1,9 +1,10 @@ #! /bin/sh # depcomp - compile a program generating dependencies as side-effects -scriptversion=2013-05-30.07; # UTC +scriptversion=2011-12-04.11; # UTC -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010, +# 2011 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -27,9 +28,9 @@ scriptversion=2013-05-30.07; # UTC case $1 in '') - echo "$0: No command. Try '$0 --help' for more information." 1>&2 - exit 1; - ;; + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] @@ -39,8 +40,8 @@ as side-effects. Environment variables: depmode Dependency tracking mode. - source Source file read by 'PROGRAMS ARGS'. - object Object file output by 'PROGRAMS ARGS'. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. @@ -56,66 +57,6 @@ EOF ;; esac -# Get the directory component of the given path, and save it in the -# global variables '$dir'. Note that this directory component will -# be either empty or ending with a '/' character. This is deliberate. -set_dir_from () -{ - case $1 in - */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; - *) dir=;; - esac -} - -# Get the suffix-stripped basename of the given path, and save it the -# global variable '$base'. -set_base_from () -{ - base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` -} - -# If no dependency file was actually created by the compiler invocation, -# we still have to create a dummy depfile, to avoid errors with the -# Makefile "include basename.Plo" scheme. -make_dummy_depfile () -{ - echo "#dummy" > "$depfile" -} - -# Factor out some common post-processing of the generated depfile. -# Requires the auxiliary global variable '$tmpdepfile' to be set. -aix_post_process_depfile () -{ - # If the compiler actually managed to produce a dependency file, - # post-process it. - if test -f "$tmpdepfile"; then - # Each line is of the form 'foo.o: dependency.h'. - # Do two passes, one to just change these to - # $object: dependency.h - # and one to simply output - # dependency.h: - # which is needed to avoid the deleted-header problem. - { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" - sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" - } > "$depfile" - rm -f "$tmpdepfile" - else - make_dummy_depfile - fi -} - -# A tabulation character. -tab=' ' -# A newline character. -nl=' -' -# Character ranges might be problematic outside the C locale. -# These definitions help. -upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ -lower=abcdefghijklmnopqrstuvwxyz -digits=0123456789 -alpha=${upper}${lower} - if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 @@ -128,9 +69,6 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" -# Avoid interferences from the environment. -gccflag= dashmflag= - # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case @@ -142,32 +80,26 @@ if test "$depmode" = hp; then fi if test "$depmode" = dashXmstdout; then - # This is just like dashmstdout with a different argument. - dashmflag=-xM - depmode=dashmstdout + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then - # This is just like msvisualcpp but w/o cygpath translation. - # Just convert the backslash-escaped backslashes to single forward - # slashes to satisfy depend.m4 - cygpath_u='sed s,\\\\,/,g' - depmode=msvisualcpp + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then - # This is just like msvc7 but w/o cygpath translation. - # Just convert the backslash-escaped backslashes to single forward - # slashes to satisfy depend.m4 - cygpath_u='sed s,\\\\,/,g' - depmode=msvc7 -fi - -if test "$depmode" = xlc; then - # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. - gccflag=-qmakedep=gcc,-MF - depmode=gcc + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 fi case "$depmode" in @@ -190,7 +122,8 @@ gcc3) done "$@" stat=$? - if test $stat -ne 0; then + if test $stat -eq 0; then : + else rm -f "$tmpdepfile" exit $stat fi @@ -198,17 +131,13 @@ gcc3) ;; gcc) -## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. -## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. -## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like -## -MM, not -M (despite what the docs say). Also, it might not be -## supported by the other compilers which use the 'gcc' depmode. +## -MM, not -M (despite what the docs say). ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then @@ -216,31 +145,33 @@ gcc) fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? - if test $stat -ne 0; then + if test $stat -eq 0; then : + else rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" - # The second -e expression handles DOS-style file names with drive - # letters. + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" -## This next piece of magic avoids the "deleted header file" problem. +## This next piece of magic avoids the `deleted header file' problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. -## Some versions of gcc put a space before the ':'. On the theory + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. - tr ' ' "$nl" < "$tmpdepfile" \ - | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ - | sed -e 's/$/ :/' >> "$depfile" + sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; @@ -258,7 +189,8 @@ sgi) "$@" -MDupdate "$tmpdepfile" fi stat=$? - if test $stat -ne 0; then + if test $stat -eq 0; then : + else rm -f "$tmpdepfile" exit $stat fi @@ -266,41 +198,43 @@ sgi) if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; - # the IRIX cc adds comments like '#:fec' to the end of the + # the IRIX cc adds comments like `#:fec' to the end of the # dependency line. - tr ' ' "$nl" < "$tmpdepfile" \ - | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ - | tr "$nl" ' ' >> "$depfile" + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> "$depfile" echo >> "$depfile" + # The second pass generates a dummy entry for each header file. - tr ' ' "$nl" < "$tmpdepfile" \ - | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ - >> "$depfile" + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" else - make_dummy_depfile + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; -xlc) - # This case exists only to let depend.m4 do its work. It works by - # looking at the text of this script. This case will never be run, - # since it is checked for above. - exit 1 - ;; - aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the - # current directory. Also, the AIX compiler puts '$object:' at the + # current directory. Also, the AIX compiler puts `$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. - set_dir_from "$object" - set_base_from "$object" + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u @@ -313,7 +247,9 @@ aix) "$@" -M fi stat=$? - if test $stat -ne 0; then + + if test $stat -eq 0; then : + else rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi @@ -322,100 +258,44 @@ aix) do test -f "$tmpdepfile" && break done - aix_post_process_depfile - ;; - -tcc) - # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 - # FIXME: That version still under development at the moment of writing. - # Make that this statement remains true also for stable, released - # versions. - # It will wrap lines (doesn't matter whether long or short) with a - # trailing '\', as in: - # - # foo.o : \ - # foo.c \ - # foo.h \ - # - # It will put a trailing '\' even on the last line, and will use leading - # spaces rather than leading tabs (at least since its commit 0394caf7 - # "Emit spaces for -MD"). - "$@" -MD -MF "$tmpdepfile" - stat=$? - if test $stat -ne 0; then - rm -f "$tmpdepfile" - exit $stat + if test -f "$tmpdepfile"; then + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" fi - rm -f "$depfile" - # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. - # We have to change lines of the first kind to '$object: \'. - sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" - # And for each line of the second kind, we have to emit a 'dep.h:' - # dummy dependency, to avoid the deleted-header problem. - sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; -## The order of this option in the case statement is important, since the -## shell code in configure will try each of these formats in the order -## listed in this file. A plain '-MD' option would be understood by many -## compilers, so we must ensure this comes after the gcc and icc options. -pgcc) - # Portland's C compiler understands '-MD'. - # Will always output deps to 'file.d' where file is the root name of the - # source file under compilation, even if file resides in a subdirectory. - # The object file name does not affect the name of the '.d' file. - # pgcc 10.2 will output +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output # foo.o: sub/foo.c sub/foo.h - # and will wrap long lines using '\' : + # and will wrap long lines using \ : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... - set_dir_from "$object" - # Use the source, not the object, to determine the base name, since - # that's sadly what pgcc will do too. - set_base_from "$source" - tmpdepfile=$base.d - - # For projects that build the same source file twice into different object - # files, the pgcc approach of using the *source* file root name can cause - # problems in parallel builds. Use a locking strategy to avoid stomping on - # the same $tmpdepfile. - lockdir=$base.d-lock - trap " - echo '$0: caught signal, cleaning up...' >&2 - rmdir '$lockdir' - exit 1 - " 1 2 13 15 - numtries=100 - i=$numtries - while test $i -gt 0; do - # mkdir is a portable test-and-set. - if mkdir "$lockdir" 2>/dev/null; then - # This process acquired the lock. - "$@" -MD - stat=$? - # Release the lock. - rmdir "$lockdir" - break - else - # If the lock is being held by a different process, wait - # until the winning process is done or we timeout. - while test -d "$lockdir" && test $i -gt 0; do - sleep 1 - i=`expr $i - 1` - done - fi - i=`expr $i - 1` - done - trap - 1 2 13 15 - if test $i -le 0; then - echo "$0: failed to acquire lock after $numtries attempts" >&2 - echo "$0: check lockdir '$lockdir'" >&2 - exit 1 - fi - if test $stat -ne 0; then + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else rm -f "$tmpdepfile" exit $stat fi @@ -427,8 +307,8 @@ pgcc) sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. - sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ - | sed -e 's/$/ :/' >> "$depfile" + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; @@ -439,8 +319,9 @@ hp2) # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. - set_dir_from "$object" - set_base_from "$object" + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d @@ -451,7 +332,8 @@ hp2) "$@" +Maked fi stat=$? - if test $stat -ne 0; then + if test $stat -eq 0; then : + else rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi @@ -461,61 +343,77 @@ hp2) test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then - sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" - # Add 'dependent.h:' lines. + sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" + # Add `dependent.h:' lines. sed -ne '2,${ - s/^ *// - s/ \\*$// - s/$/:/ - p - }' "$tmpdepfile" >> "$depfile" + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" else - make_dummy_depfile + echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) - # The Tru64 compiler uses -MD to generate dependencies as a side - # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. - # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put - # dependencies in 'foo.d' instead, so we check for that too. - # Subdirectories are respected. - set_dir_from "$object" - set_base_from "$object" - - if test "$libtool" = yes; then - # Libtool generates 2 separate objects for the 2 libraries. These - # two compilations output dependencies in $dir.libs/$base.o.d and - # in $dir$base.o.d. We have to check for both files, because - # one of the two compilations can be disabled. We should prefer - # $dir$base.o.d over $dir.libs/$base.o.d because the latter is - # automatically cleaned when .libs/ is deleted, while ignoring - # the former would cause a distcleancheck panic. - tmpdepfile1=$dir$base.o.d # libtool 1.5 - tmpdepfile2=$dir.libs/$base.o.d # Likewise. - tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 - "$@" -Wc,-MD - else - tmpdepfile1=$dir$base.d - tmpdepfile2=$dir$base.d - tmpdepfile3=$dir$base.d - "$@" -MD - fi - - stat=$? - if test $stat -ne 0; then - rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" - exit $stat - fi - - for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" - do - test -f "$tmpdepfile" && break - done - # Same post-processing that is required for AIX mode. - aix_post_process_depfile - ;; + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a + # static library. This mechanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two + # compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 + tmpdepfile2=$dir$base.o.d # libtool 1.5 + tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 + tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.o.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + tmpdepfile4=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; msvc7) if test "$libtool" = yes; then @@ -526,7 +424,8 @@ msvc7) "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" - if test $stat -ne 0; then + if test "$stat" = 0; then : + else rm -f "$tmpdepfile" exit $stat fi @@ -544,15 +443,14 @@ msvc7) p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g -s/\(.*\)/'"$tab"'\1 \\/p +s/\(.*\)/ \1 \\/p s/.\(.*\) \\/\1:/ H $ { - s/.*/'"$tab"'/ + s/.*/ / G p }' >> "$depfile" - echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; @@ -580,7 +478,7 @@ dashmstdout) shift fi - # Remove '-o $object'. + # Remove `-o $object'. IFS=" " for arg do @@ -600,18 +498,18 @@ dashmstdout) done test -z "$dashmflag" && dashmflag=-M - # Require at least two characters before searching for ':' + # Require at least two characters before searching for `:' # in the target name. This is to cope with DOS-style filenames: - # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. "$@" $dashmflag | - sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" - # Some versions of the HPUX 10.20 sed can't process this sed invocation - # correctly. Breaking it into two sed invocations is a workaround. - tr ' ' "$nl" < "$tmpdepfile" \ - | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ - | sed -e 's/$/ :/' >> "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; @@ -664,12 +562,11 @@ makedepend) # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" - # Some versions of the HPUX 10.20 sed can't process the last invocation - # correctly. Breaking it into two sed invocations is a workaround. - sed '1,2d' "$tmpdepfile" \ - | tr ' ' "$nl" \ - | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ - | sed -e 's/$/ :/' >> "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; @@ -686,7 +583,7 @@ cpp) shift fi - # Remove '-o $object'. + # Remove `-o $object'. IFS=" " for arg do @@ -705,10 +602,10 @@ cpp) esac done - "$@" -E \ - | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ - -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ - | sed '$ s: \\$::' > "$tmpdepfile" + "$@" -E | + sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" @@ -740,23 +637,23 @@ msvisualcpp) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") - set fnord "$@" - shift - shift - ;; + set fnord "$@" + shift + shift + ;; *) - set fnord "$@" "$arg" - shift - shift - ;; + set fnord "$@" "$arg" + shift + shift + ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" - sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" - echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; diff --git a/diagrams/netdata-for-ephemeral-nodes.xml b/diagrams/netdata-for-ephemeral-nodes.xml new file mode 100644 index 000000000..8606170e5 --- /dev/null +++ b/diagrams/netdata-for-ephemeral-nodes.xml @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/diagrams/netdata-proxies-example.xml b/diagrams/netdata-proxies-example.xml new file mode 100644 index 000000000..956bdaf10 --- /dev/null +++ b/diagrams/netdata-proxies-example.xml @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/install-sh b/install-sh index 0b0fdcbba..a9244eb07 100755 --- a/install-sh +++ b/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2013-12-25.23; # UTC +scriptversion=2011-01-19.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -35,21 +35,25 @@ scriptversion=2013-12-25.23; # UTC # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent -# 'make' implicit rules from creating a file called install from it +# `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. -tab=' ' nl=' ' -IFS=" $tab$nl" +IFS=" "" $nl" -# Set DOITPROG to "echo" to test this script. +# set DOITPROG to echo to test this script +# Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} -doit_exec=${doit:-exec} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi # Put in absolute file names if you don't have them in your path; # or use environment vars. @@ -64,6 +68,17 @@ mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + posix_mkdir= # Desired mode of installed file. @@ -82,7 +97,7 @@ dir_arg= dst_arg= copy_on_change=false -is_target_a_directory=possibly +no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE @@ -122,57 +137,46 @@ while test $# -ne 0; do -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" - shift;; + shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 - case $mode in - *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) - echo "$0: invalid mode: $mode" >&2 - exit 1;; - esac - shift;; + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; -o) chowncmd="$chownprog $2" - shift;; + shift;; -s) stripcmd=$stripprog;; - -t) - is_target_a_directory=always - dst_arg=$2 - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - shift;; + -t) dst_arg=$2 + # Protect names problematic for `test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; - -T) is_target_a_directory=never;; + -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; - --) shift - break;; + --) shift + break;; - -*) echo "$0: invalid option: $1" >&2 - exit 1;; + -*) echo "$0: invalid option: $1" >&2 + exit 1;; *) break;; esac shift done -# We allow the use of options -d and -T together, by making -d -# take the precedence; this is for compatibility with GNU install. - -if test -n "$dir_arg"; then - if test -n "$dst_arg"; then - echo "$0: target directory not allowed when installing a directory." >&2 - exit 1 - fi -fi - if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. @@ -186,7 +190,7 @@ if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then fi shift # arg dst_arg=$arg - # Protect names problematic for 'test' and other utilities. + # Protect names problematic for `test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac @@ -198,20 +202,11 @@ if test $# -eq 0; then echo "$0: no input file specified." >&2 exit 1 fi - # It's OK to call 'install-sh -d' without argument. + # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi -if test -z "$dir_arg"; then - if test $# -gt 1 || test "$is_target_a_directory" = always; then - if test ! -d "$dst_arg"; then - echo "$0: $dst_arg: Is not a directory." >&2 - exit 1 - fi - fi -fi - if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 @@ -228,16 +223,16 @@ if test -z "$dir_arg"; then *[0-7]) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw='% 200' + u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw=,u+rw + u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac @@ -245,7 +240,7 @@ fi for src do - # Protect names problematic for 'test' and other utilities. + # Protect names problematic for `test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac @@ -274,15 +269,41 @@ do # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then - if test "$is_target_a_directory" = never; then - echo "$0: $dst_arg: Is a directory" >&2 - exit 1 + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else - dstdir=`dirname "$dst"` + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + test -d "$dstdir" dstdir_status=$? fi @@ -293,74 +314,74 @@ do if test $dstdir_status != 0; then case $posix_mkdir in '') - # Create intermediate dirs using mode 755 as modified by the umask. - # This is like FreeBSD 'install' as of 1997-10-28. - umask=`umask` - case $stripcmd.$umask in - # Optimize common cases. - *[2367][2367]) mkdir_umask=$umask;; - .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; - - *[0-7]) - mkdir_umask=`expr $umask + 22 \ - - $umask % 100 % 40 + $umask % 20 \ - - $umask % 10 % 4 + $umask % 2 - `;; - *) mkdir_umask=$umask,go-w;; - esac - - # With -d, create the new directory with the user-specified mode. - # Otherwise, rely on $mkdir_umask. - if test -n "$dir_arg"; then - mkdir_mode=-m$mode - else - mkdir_mode= - fi - - posix_mkdir=false - case $umask in - *[123567][0-7][0-7]) - # POSIX mkdir -p sets u+wx bits regardless of umask, which - # is incompatible with FreeBSD 'install' when (umask & 300) != 0. - ;; - *) - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 - - if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/d" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null - fi - trap '' 0;; - esac;; + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writeable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; esac if $posix_mkdir && ( - umask $mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else @@ -370,51 +391,53 @@ do # directory the slow way, step by step, checking for races as we go. case $dstdir in - /*) prefix='/';; - [-=\(\)!]*) prefix='./';; - *) prefix='';; + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; esac + eval "$initialize_posix_glob" + oIFS=$IFS IFS=/ - set -f + $posix_glob set -f set fnord $dstdir shift - set +f + $posix_glob set +f IFS=$oIFS prefixes= for d do - test X"$d" = X && continue - - prefix=$prefix$d - if test -d "$prefix"; then - prefixes= - else - if $posix_mkdir; then - (umask=$mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break - # Don't fail if two instances are running concurrently. - test -d "$prefix" || exit 1 - else - case $prefix in - *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; - *) qprefix=$prefix;; - esac - prefixes="$prefixes '$qprefix'" - fi - fi - prefix=$prefix/ + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ done if test -n "$prefixes"; then - # Don't fail if two instances are running concurrently. - (umask $mkdir_umask && - eval "\$doit_exec \$mkdirprog $prefixes") || - test -d "$dstdir" || exit 1 - obsolete_mkdir_used=true + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true fi fi fi @@ -449,12 +472,15 @@ do # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && - old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && - new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && - set -f && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && - set +f && + $posix_glob set +f && + test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then @@ -467,24 +493,24 @@ do # to itself, or perhaps because mv is so ancient that it does not # support -f. { - # Now remove or move aside any old file at destination location. - # We try this two ways since rm can't unlink itself on some - # systems and the destination file might be busy for other - # reasons. In this case, the final cleanup might fail but the new - # file should still install successfully. - { - test ! -f "$dst" || - $doit $rmcmd -f "$dst" 2>/dev/null || - { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } - } || - { echo "$0: cannot unlink or rename $dst" >&2 - (exit 1); exit 1 - } - } && - - # Now rename the file to the real destination. - $doit $mvcmd "$dsttmp" "$dst" + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 diff --git a/installer/functions.sh b/installer/functions.sh new file mode 100644 index 000000000..36d10ec70 --- /dev/null +++ b/installer/functions.sh @@ -0,0 +1,344 @@ +# no shebang necessary - this is a library to be sourced + +# ----------------------------------------------------------------------------- +# checking the availability of commands + +which_cmd() { + which "${1}" 2>/dev/null || \ + command -v "${1}" 2>/dev/null +} + +check_cmd() { + which_cmd "${1}" >/dev/null 2>&1 && return 0 + return 1 +} + + +# ----------------------------------------------------------------------------- + +setup_terminal() { + TPUT_RESET="" + TPUT_BLACK="" + TPUT_RED="" + TPUT_GREEN="" + TPUT_YELLOW="" + TPUT_BLUE="" + TPUT_PURPLE="" + TPUT_CYAN="" + TPUT_WHITE="" + TPUT_BGBLACK="" + TPUT_BGRED="" + TPUT_BGGREEN="" + TPUT_BGYELLOW="" + TPUT_BGBLUE="" + TPUT_BGPURPLE="" + TPUT_BGCYAN="" + TPUT_BGWHITE="" + TPUT_BOLD="" + TPUT_DIM="" + TPUT_UNDERLINED="" + TPUT_BLINK="" + TPUT_INVERTED="" + TPUT_STANDOUT="" + TPUT_BELL="" + TPUT_CLEAR="" + + # Is stderr on the terminal? If not, then fail + test -t 2 || return 1 + + if check_cmd tput + then + if [ $(( $(tput colors 2>/dev/null) )) -ge 8 ] + then + # Enable colors + TPUT_RESET="$(tput sgr 0)" + TPUT_BLACK="$(tput setaf 0)" + TPUT_RED="$(tput setaf 1)" + TPUT_GREEN="$(tput setaf 2)" + TPUT_YELLOW="$(tput setaf 3)" + TPUT_BLUE="$(tput setaf 4)" + TPUT_PURPLE="$(tput setaf 5)" + TPUT_CYAN="$(tput setaf 6)" + TPUT_WHITE="$(tput setaf 7)" + TPUT_BGBLACK="$(tput setab 0)" + TPUT_BGRED="$(tput setab 1)" + TPUT_BGGREEN="$(tput setab 2)" + TPUT_BGYELLOW="$(tput setab 3)" + TPUT_BGBLUE="$(tput setab 4)" + TPUT_BGPURPLE="$(tput setab 5)" + TPUT_BGCYAN="$(tput setab 6)" + TPUT_BGWHITE="$(tput setab 7)" + TPUT_BOLD="$(tput bold)" + TPUT_DIM="$(tput dim)" + TPUT_UNDERLINED="$(tput smul)" + TPUT_BLINK="$(tput blink)" + TPUT_INVERTED="$(tput rev)" + TPUT_STANDOUT="$(tput smso)" + TPUT_BELL="$(tput bel)" + TPUT_CLEAR="$(tput clear)" + fi + fi + + return 0 +} +setup_terminal + +progress() { + echo >&2 " --- ${TPUT_DIM}${TPUT_BOLD}${*}${TPUT_RESET} --- " +} + +# ----------------------------------------------------------------------------- + +netdata_banner() { + local l1=" ^" \ + l2=" |.-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-" \ + l3=" | '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' " \ + l4=" +----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+--->" \ + sp=" " \ + netdata="netdata" start end msg="${*}" chartcolor="${TPUT_DIM}" + + [ ${#msg} -lt ${#netdata} ] && msg="${msg}${sp:0:$(( ${#netdata} - ${#msg}))}" + [ ${#msg} -gt $(( ${#l2} - 20 )) ] && msg="${msg:0:$(( ${#l2} - 23 ))}..." + + start="$(( ${#l2} / 2 - 4 ))" + [ $(( start + ${#msg} + 4 )) -gt ${#l2} ] && start=$((${#l2} - ${#msg} - 4)) + end=$(( ${start} + ${#msg} + 4 )) + + echo >&2 + echo >&2 "${chartcolor}${l1}${TPUT_RESET}" + echo >&2 "${chartcolor}${l2:0:start}${sp:0:2}${TPUT_RESET}${TPUT_BOLD}${TPUT_GREEN}${netdata}${TPUT_RESET}${chartcolor}${sp:0:$((end - start - 2 - ${#netdata}))}${l2:end:$((${#l2} - end))}${TPUT_RESET}" + echo >&2 "${chartcolor}${l3:0:start}${sp:0:2}${TPUT_RESET}${TPUT_BOLD}${TPUT_CYAN}${msg}${TPUT_RESET}${chartcolor}${sp:0:2}${l3:end:$((${#l2} - end))}${TPUT_RESET}" + echo >&2 "${chartcolor}${l4}${TPUT_RESET}" + echo >&2 +} + +# ----------------------------------------------------------------------------- +# portable service command + +service_cmd="$(which_cmd service)" +systemctl_cmd="$(which_cmd systemctl)" +service() { + local cmd="${1}" action="${2}" + + if [ ! -z "${service_cmd}" ] + then + run "${service_cmd}" "${cmd}" "${action}" + return $? + elif [ ! -z "${systemctl_cmd}" ] + then + run "${systemctl_cmd}" "${action}" "${cmd}" + return $? + fi + return 1 +} + +# ----------------------------------------------------------------------------- + +run_ok() { + printf >&2 "${TPUT_BGGREEN}${TPUT_WHITE}${TPUT_BOLD} OK ${TPUT_RESET} ${*} \n\n" +} + +run_failed() { + printf >&2 "${TPUT_BGRED}${TPUT_WHITE}${TPUT_BOLD} FAILED ${TPUT_RESET} ${*} \n\n" +} + +run_logfile="/dev/null" +run() { + local user="${USER}" dir="$(basename "${PWD}")" info info_console + + if [ "${UID}" = "0" ] + then + info="[root ${dir}]# " + info_console="[${TPUT_DIM}${dir}${TPUT_RESET}]# " + else + info="[${user} ${dir}]$ " + info_console="[${TPUT_DIM}${dir}${TPUT_RESET}]$ " + fi + + printf >> "${run_logfile}" "${info}" + printf >> "${run_logfile}" "%q " "${@}" + printf >> "${run_logfile}" " ... " + + printf >&2 "${info_console}${TPUT_BOLD}${TPUT_YELLOW}" + printf >&2 "%q " "${@}" + printf >&2 "${TPUT_RESET}\n" + + "${@}" + + local ret=$? + if [ ${ret} -ne 0 ] + then + run_failed + printf >> "${run_logfile}" "FAILED with exit code ${ret}\n" + else + run_ok + printf >> "${run_logfile}" "OK\n" + fi + + return ${ret} +} + +portable_add_user() { + local username="${1}" + + getent passwd "${username}" > /dev/null 2>&1 + [ $? -eq 0 ] && echo >&2 "User '${username}' already exists." && return 0 + + echo >&2 "Adding ${username} user account ..." + + local nologin="$(which nologin 2>/dev/null || command -v nologin 2>/dev/null || echo '/bin/false')" + + # Linux + if check_cmd useradd + then + run useradd -r -g "${username}" -c "${username}" -s "${nologin}" -d / "${username}" && return 0 + fi + + # FreeBSD + if check_cmd pw + then + run pw useradd "${username}" -d / -g "${username}" -s "${nologin}" && return 0 + fi + + # BusyBox + if check_cmd adduser + then + run adduser -D -G "${username}" "${username}" && return 0 + fi + + echo >&2 "Failed to add ${username} user account !" + + return 1 +} + +portable_add_group() { + local groupname="${1}" + + getent group "${groupname}" > /dev/null 2>&1 + [ $? -eq 0 ] && echo >&2 "Group '${groupname}' already exists." && return 0 + + echo >&2 "Adding ${groupname} user group ..." + + # Linux + if check_cmd groupadd + then + run groupadd -r "${groupname}" && return 0 + fi + + # FreeBSD + if check_cmd pw + then + run pw groupadd "${groupname}" && return 0 + fi + + # BusyBox + if check_cmd addgroup + then + run addgroup "${groupname}" && return 0 + fi + + echo >&2 "Failed to add ${groupname} user group !" + return 1 +} + +portable_add_user_to_group() { + local groupname="${1}" username="${2}" + + getent group "${groupname}" > /dev/null 2>&1 + [ $? -ne 0 ] && echo >&2 "Group '${groupname}' does not exist." && return 1 + + # find the user is already in the group + local users=$(getent group "${groupname}" | cut -d ':' -f 4) + if [[ ",${users}," =~ ,${username}, ]] + then + # username is already there + echo >&2 "User '${username}' is already in group '${groupname}'." + return 0 + else + # username is not in group + echo >&2 "Adding ${username} user to the ${groupname} group ..." + + # Linux + if check_cmd usermod + then + run usermod -a -G "${groupname}" "${username}" && return 0 + fi + + # FreeBSD + if check_cmd pw + then + run pw groupmod "${groupname}" -m "${username}" && return 0 + fi + + # BusyBox + if check_cmd addgroup + then + run addgroup "${username}" "${groupname}" && return 0 + fi + + echo >&2 "Failed to add user ${username} to group ${groupname} !" + return 1 + fi +} + +iscontainer() { + # man systemd-detect-virt + local cmd=$(which_cmd systemd-detect-virt) + if [ ! -z "${cmd}" -a -x "${cmd}" ] + then + "${cmd}" --container >/dev/null 2>&1 && return 0 + fi + + # /proc/1/sched exposes the host's pid of our init ! + # http://stackoverflow.com/a/37016302 + local pid=$( cat /proc/1/sched 2>/dev/null | head -n 1 | { IFS='(),#:' read name pid th threads; echo $pid; } ) + pid=$(( pid + 0 )) + [ ${pid} -ne 1 ] && return 0 + + # lxc sets environment variable 'container' + [ ! -z "${container}" ] && return 0 + + # docker creates /.dockerenv + # http://stackoverflow.com/a/25518345 + [ -f "/.dockerenv" ] && return 0 + + # ubuntu and debian supply /bin/running-in-container + # https://www.apt-browse.org/browse/ubuntu/trusty/main/i386/upstart/1.12.1-0ubuntu4/file/bin/running-in-container + if [ -x "/bin/running-in-container" ] + then + "/bin/running-in-container" >/dev/null 2>&1 && return 0 + fi + + return 1 +} + +issystemd() { + local pids p myns ns systemctl + + # if the directory /etc/systemd/system does not exit, it is not systemd + [ ! -d /etc/systemd/system ] && return 1 + + # if there is no systemctl command, it is not systemd + systemctl=$(which systemctl 2>/dev/null || command -v systemctl 2>/dev/null) + [ -z "${systemctl}" -o ! -x "${systemctl}" ] && return 1 + + # if pid 1 is systemd, it is systemd + [ "$(basename $(readlink /proc/1/exe) 2>/dev/null)" = "systemd" ] && return 0 + + # if systemd is not running, it is not systemd + pids=$(pidof systemd 2>/dev/null) + [ -z "${pids}" ] && return 1 + + # check if the running systemd processes are not in our namespace + myns="$(readlink /proc/self/ns/pid 2>/dev/null)" + for p in ${pids} + do + ns="$(readlink /proc/${p}/ns/pid 2>/dev/null)" + + # if pid of systemd is in our namespace, it is systemd + [ ! -z "${myns}" && "${myns}" = "${ns}" ] && return 0 + done + + # else, it is not systemd + return 1 +} diff --git a/m4/ax_c_lto.m4 b/m4/ax_c_lto.m4 new file mode 100644 index 000000000..7e6bc0119 --- /dev/null +++ b/m4/ax_c_lto.m4 @@ -0,0 +1,21 @@ +# AC_C_LTO +# ------------- +# Define HAVE_LTO if -flto works. +AN_IDENTIFIER([lto], [AC_C_LTO]) +AC_DEFUN([AC_C_LTO], +[AC_CACHE_CHECK([if -flto builds executables], ac_cv_c_lto, +[AC_RUN_IFELSE( + [AC_LANG_SOURCE( + [[#include + int main(int argc, char **argv) { + return 0; + } + ]])], + [ac_cv_c_lto=yes], + [ac_cv_c_lto=no], + [ac_cv_c_lto=${ac_cv_c_lto_cross_compile}])]) +if test "${ac_cv_c_lto}" = "yes"; then + AC_DEFINE([HAVE_LTO], 1, + [Define to 1 if -flto works.]) +fi +])# AC_C_LTO diff --git a/missing b/missing index f62bbae30..86a8fc31e 100755 --- a/missing +++ b/missing @@ -1,10 +1,11 @@ #! /bin/sh -# Common wrapper for a few potentially missing GNU programs. +# Common stub for a few missing GNU programs while installing. -scriptversion=2013-10-28.13; # UTC +scriptversion=2012-01-06.13; # UTC -# Copyright (C) 1996-2014 Free Software Foundation, Inc. -# Originally written by Fran,cois Pinard , 1996. +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, +# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,40 +26,68 @@ scriptversion=2013-10-28.13; # UTC # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then - echo 1>&2 "Try '$0 --help' for more information" + echo 1>&2 "Try \`$0 --help' for more information" exit 1 fi -case $1 in +run=: +sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' +sed_minuso='s/.* -o \([^ ]*\).*/\1/p' - --is-lightweight) - # Used by our autoconf macros to check whether the available missing - # script is modern enough. - exit 0 - ;; +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi - --run) - # Back-compat with the calling convention used by older automake. - shift - ;; +msg="missing on your system" + +case $1 in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... -Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due -to PROGRAM being missing or too old. +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. Options: -h, --help display this help and exit -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails Supported PROGRAM values: - aclocal autoconf autoheader autom4te automake makeinfo - bison yacc flex lex help2man + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + autom4te touch the output file, or create a stub one + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + yacc create \`y.tab.[ch]', if possible, from existing .[ch] -Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and -'g' are ignored when checking the name. +Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and +\`g' are ignored when checking the name. Send bug reports to ." exit $? @@ -70,141 +99,228 @@ Send bug reports to ." ;; -*) - echo 1>&2 "$0: unknown '$1' option" - echo 1>&2 "Try '$0 --help' for more information" + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" exit 1 ;; esac -# Run the given program, remember its exit status. -"$@"; st=$? - -# If it succeeded, we are done. -test $st -eq 0 && exit 0 - -# Also exit now if we it failed (or wasn't found), and '--version' was -# passed; such an option is passed most likely to detect whether the -# program is present and works. -case $2 in --version|--help) exit $st;; esac - -# Exit code 63 means version mismatch. This often happens when the user -# tries to use an ancient version of a tool on a file that requires a -# minimum version. -if test $st -eq 63; then - msg="probably too old" -elif test $st -eq 127; then - # Program was missing. - msg="missing on your system" -else - # Program was found and executed, but failed. Give up. - exit $st -fi +# normalize program name to check for. +program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). This is about non-GNU programs, so use $1 not +# $program. +case $1 in + lex*|yacc*) + # Not GNU programs, they don't have --version. + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case $program in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; -perl_URL=http://www.perl.org/ -flex_URL=http://flex.sourceforge.net/ -gnu_software_URL=http://www.gnu.org/software - -program_details () -{ - case $1 in - aclocal|automake) - echo "The '$1' program is part of the GNU Automake package:" - echo "<$gnu_software_URL/automake>" - echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" - echo "<$gnu_software_URL/autoconf>" - echo "<$gnu_software_URL/m4/>" - echo "<$perl_URL>" - ;; - autoconf|autom4te|autoheader) - echo "The '$1' program is part of the GNU Autoconf package:" - echo "<$gnu_software_URL/autoconf/>" - echo "It also requires GNU m4 and Perl in order to run:" - echo "<$gnu_software_URL/m4/>" - echo "<$perl_URL>" - ;; - esac -} - -give_advice () -{ - # Normalize program name to check for. - normalized_program=`echo "$1" | sed ' - s/^gnu-//; t - s/^gnu//; t - s/^g//; t'` - - printf '%s\n' "'$1' is $msg." - - configure_deps="'configure.ac' or m4 files included by 'configure.ac'" - case $normalized_program in - autoconf*) - echo "You should only need it if you modified 'configure.ac'," - echo "or m4 files included by it." - program_details 'autoconf' - ;; - autoheader*) - echo "You should only need it if you modified 'acconfig.h' or" - echo "$configure_deps." - program_details 'autoheader' - ;; - automake*) - echo "You should only need it if you modified 'Makefile.am' or" - echo "$configure_deps." - program_details 'automake' - ;; - aclocal*) - echo "You should only need it if you modified 'acinclude.m4' or" - echo "$configure_deps." - program_details 'aclocal' - ;; - autom4te*) - echo "You might have modified some maintainer files that require" - echo "the 'autom4te' program to be rebuilt." - program_details 'autom4te' - ;; - bison*|yacc*) - echo "You should only need it if you modified a '.y' file." - echo "You may want to install the GNU Bison package:" - echo "<$gnu_software_URL/bison/>" - ;; - lex*|flex*) - echo "You should only need it if you modified a '.l' file." - echo "You may want to install the Fast Lexical Analyzer package:" - echo "<$flex_URL>" - ;; - help2man*) - echo "You should only need it if you modified a dependency" \ - "of a man page." - echo "You may want to install the GNU Help2man package:" - echo "<$gnu_software_URL/help2man/>" + autoheader*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case $f in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files ;; - makeinfo*) - echo "You should only need it if you modified a '.texi' file, or" - echo "any other file indirectly affecting the aspect of the manual." - echo "You might want to install the Texinfo package:" - echo "<$gnu_software_URL/texinfo/>" - echo "The spurious makeinfo call might also be the consequence of" - echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" - echo "want to install GNU make:" - echo "<$gnu_software_URL/make/>" - ;; - *) - echo "You might have modified some files without having the proper" - echo "tools for further handling them. Check the 'README' file, it" - echo "often tells you about the needed prerequisites for installing" - echo "this package. You may also peek at any GNU archive site, in" - echo "case some other package contains this missing '$1' program." - ;; - esac -} - -give_advice "$1" | sed -e '1s/^/WARNING: /' \ - -e '2,$s/^/ /' >&2 - -# Propagate the correct exit status (expected to be 127 for a program -# not found, 63 for a program that failed due to version mismatch). -exit $st + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te*) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison*|yacc*) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if test $# -ne 1; then + eval LASTARG=\${$#} + case $LASTARG in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if test ! -f y.tab.h; then + echo >y.tab.h + fi + if test ! -f y.tab.c; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex*|flex*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if test $# -ne 1; then + eval LASTARG=\${$#} + case $LASTARG in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if test ! -f lex.yy.c; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit $? + fi + ;; + + makeinfo*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n ' + /^@setfilename/{ + s/.* \([^ ]*\) *$/\1/ + p + q + }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + # If the file does not exist, the user really needs makeinfo; + # let's fail without touching anything. + test -f $file || exit 1 + touch $file + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) diff --git a/netdata-installer.sh b/netdata-installer.sh index fa69de196..6b672a242 100755 --- a/netdata-installer.sh +++ b/netdata-installer.sh @@ -1,11 +1,42 @@ #!/usr/bin/env bash +export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin" + +netdata_source_dir="$(pwd)" +installer_dir="$(dirname "${0}")" + +if [ "${netdata_source_dir}" != "${installer_dir}" -a "${installer_dir}" != "." ] + then + echo >&2 "Warninng: you are currently in '${netdata_source_dir}' but the installer is in '${installer_dir}'." +fi + + +# ----------------------------------------------------------------------------- # reload the user profile + [ -f /etc/profile ] && . /etc/profile -export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin" +# make sure /etc/profile does not change our current directory +cd "${netdata_source_dir}" || exit 1 + + +# ----------------------------------------------------------------------------- +# load the required functions + +if [ -f "${installer_dir}/installer/functions.sh" ] + then + source "${installer_dir}/installer/functions.sh" || exit 1 +else + source "${netdata_source_dir}/installer/functions.sh" || exit 1 +fi + +# make sure we save all commands we run +run_logfile="netdata-installer.log" + +# ----------------------------------------------------------------------------- # fix PKG_CHECK_MODULES error + if [ -d /usr/share/aclocal ] then ACLOCAL_PATH=${ACLOCAL_PATH-/usr/share/aclocal} @@ -18,7 +49,7 @@ umask 002 # Be nice on production environments renice 19 $$ >/dev/null 2>/dev/null -processors=$(cat /proc/cpuinfo | grep ^processor | wc -l) +processors=$(cat /proc/cpuinfo 2>/dev/null | grep ^processor | wc -l) [ $(( processors )) -lt 1 ] && processors=1 # you can set CFLAGS before running installer @@ -35,54 +66,17 @@ printf "\n" >>netdata-installer.log REINSTALL_PWD="${PWD}" REINSTALL_COMMAND="$(printf "%q " "$0" "${@}"; printf "\n")" -banner() { - local l1=" ^" \ - l2=" |.-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-" \ - l3=" | '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' " \ - l4=" +----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+--->" \ - sp=" " \ - netdata="netdata" start end msg="${*}" - - [ ${#msg} -lt ${#netdata} ] && msg="${msg}${sp:0:$(( ${#netdata} - ${#msg}))}" - [ ${#msg} -gt $(( ${#l2} - 20 )) ] && msg="${msg:0:$(( ${#l2} - 23 ))}..." - - start="$(( ${#l2} / 2 - 4 ))" - [ $(( start + ${#msg} + 4 )) -gt ${#l2} ] && start=$((${#l2} - ${#msg} - 4)) - end=$(( ${start} + ${#msg} + 4 )) - - echo >&2 - echo >&2 "${l1}" - echo >&2 "${l2:0:start}${sp:0:2}${netdata}${sp:0:$((end - start - 2 - ${#netdata}))}${l2:end:$((${#l2} - end))}" - echo >&2 "${l3:0:start}${sp:0:2}${msg}${sp:0:2}${l3:end:$((${#l2} - end))}" - echo >&2 "${l4}" - echo >&2 -} - -service="$(which service 2>/dev/null || command -v service 2>/dev/null)" -systemctl="$(which systemctl 2>/dev/null || command -v systemctl 2>/dev/null)" -service() { - local cmd="${1}" action="${2}" - - if [ ! -z "${service}" ] - then - run "${service}" "${cmd}" "${action}" - return $? - elif [ ! -z "${systemctl}" ] - then - run "${systemctl}" "${action}" "${cmd}" - return $? - fi - return 1 -} +setcap="$(which setcap 2>/dev/null || command -v setcap 2>/dev/null)" ME="$0" DONOTSTART=0 DONOTWAIT=0 NETDATA_PREFIX= LIBS_ARE_HERE=0 +NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS-}" usage() { - banner "installer command line options" + netdata_banner "installer command line options" cat < @@ -91,7 +85,7 @@ Valid are: --install /PATH/TO/INSTALL - If your give: --install /opt + If you give: --install /opt netdata will be installed in /opt/netdata --dont-start-it @@ -104,6 +98,24 @@ Valid are: Do not wait for the user to press ENTER. Start immediately building it. + --enable-plugin-freeipmi + --disable-plugin-freeipmi + + Enable/disable the FreeIPMI plugin. + Default: enable it when libipmimonitoring is available. + + --enable-plugin-nfacct + --disable-plugin-nfacct + + Enable/disable the nfacct plugin. + Default: enable it when libmnl and libnetfilter_acct are available. + + --enable-lto + --disable-lto + + Enable/disable Link-Time-Optimization + Default: enabled + --zlib-is-really-here --libs-are-really-here @@ -131,7 +143,7 @@ For the plugins, you will at least need: USAGE } -md5sum="$(which md5sum 2>/dev/null || command -v md5sum 2>/dev/null)" +md5sum="$(which md5sum 2>/dev/null || command -v md5sum 2>/dev/null || command -v md5 2>/dev/null)" get_git_config_signatures() { local x s file md5 @@ -147,7 +159,7 @@ get_git_config_signatures() { for c in $(git log --follow "conf.d/${x}" | grep ^commit | cut -d ' ' -f 2) do git checkout ${c} "conf.d/${x}" || continue - s="$(cat "conf.d/${x}" | md5sum | cut -d ' ' -f 1)" + s="$(cat "conf.d/${x}" | ${md5sum} | cut -d ' ' -f 1)" echo >>configs.signatures.tmp "${s}:${x}" echo " ${s}" done @@ -191,6 +203,30 @@ do then DONOTWAIT=1 shift 1 + elif [ "$1" = "--enable-plugin-freeipmi" ] + then + NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS} --enable-plugin-freeipmi" + shift 1 + elif [ "$1" = "--disable-plugin-freeipmi" ] + then + NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS} --disable-plugin-freeipmi" + shift 1 + elif [ "$1" = "--enable-plugin-nfacct" ] + then + NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS} --enable-plugin-nfacct" + shift 1 + elif [ "$1" = "--disable-plugin-nfacct" ] + then + NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS} --disable-plugin-nfacct" + shift 1 + elif [ "$1" = "--enable-lto" ] + then + NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS} --enable-lto" + shift 1 + elif [ "$1" = "--disable-lto" ] + then + NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS} --disable-lto" + shift 1 elif [ "$1" = "--help" -o "$1" = "-h" ] then usage @@ -208,66 +244,74 @@ do fi done -banner "real-time performance monitoring, done right!" -cat < " + eval "read >&2 -ep \$'\001${TPUT_BOLD}${TPUT_GREEN}\002Press ENTER to build and install netdata to \'\001${TPUT_CYAN}\002${NETDATA_PREFIX}\001${TPUT_YELLOW}\002\'\001${TPUT_RESET}\002 > ' -e -r REPLY" else - read -p "Press ENTER to build and install netdata to your system > " + eval "read >&2 -ep \$'\001${TPUT_BOLD}${TPUT_GREEN}\002Press ENTER to build and install netdata to your system\001${TPUT_RESET}\002 > ' -e -r REPLY" fi fi build_error() { - banner "sorry, it failed to build..." + netdata_banner "sorry, it failed to build..." cat <>netdata-installer.log "# " - printf >>netdata-installer.log "%q " "${@}" - printf >>netdata-installer.log " ... " - - printf >&2 "\n" - printf >&2 ":-----------------------------------------------------------------------------\n" - printf >&2 "Running command:\n" - printf >&2 "\n" - printf >&2 "%q " "${@}" - printf >&2 "\n" - - "${@}" - - local ret=$? - if [ ${ret} -ne 0 ] - then - printf >>netdata-installer.log "FAILED!\n" - else - printf >>netdata-installer.log "OK\n" - fi - - return ${ret} -} - if [ ${LIBS_ARE_HERE} -eq 1 ] then shift @@ -412,6 +431,11 @@ fi trap build_error EXIT + +# ----------------------------------------------------------------------------- +echo >&2 +progress "Run autotools to configure the build environment" + if [ "$have_autotools" ] then run ./autogen.sh || exit 1 @@ -421,29 +445,28 @@ run ./configure \ --prefix="${NETDATA_PREFIX}/usr" \ --sysconfdir="${NETDATA_PREFIX}/etc" \ --localstatedir="${NETDATA_PREFIX}/var" \ - --with-zlib --with-math --with-user=netdata \ + --with-zlib \ + --with-math \ + --with-user=netdata \ + ${NETDATA_CONFIGURE_OPTIONS} \ CFLAGS="${CFLAGS}" || exit 1 # remove the build_error hook trap - EXIT -if [ -f src/netdata ] - then - echo >&2 "Cleaning a possibly old compilation ..." - run make clean -fi +# ----------------------------------------------------------------------------- +progress "Cleanup compilation directory" + +[ -f src/netdata ] && run make clean + +# ----------------------------------------------------------------------------- +progress "Compile netdata" -echo >&2 "Compiling netdata ..." run make -j${processors} || exit 1 -if [ "${BASH_VERSINFO[0]}" -ge "4" ] -then - declare -A configs_signatures=() - if [ -f "configs.signatures" ] - then - source "configs.signatures" || echo >&2 "ERROR: Failed to load configs.signatures !" - fi -fi + +# ----------------------------------------------------------------------------- +progress "Migrate configuration files for node.d.plugin and charts.d.plugin" # migrate existing configuration files # for node.d and charts.d @@ -486,6 +509,36 @@ if [ -d "${NETDATA_PREFIX}/etc/netdata" ] done fi +# ----------------------------------------------------------------------------- +progress "Backup existing netdata configuration before installing it" + +if [ "${BASH_VERSINFO[0]}" -ge "4" ] +then + declare -A configs_signatures=() + if [ -f "configs.signatures" ] + then + source "configs.signatures" || echo >&2 "ERROR: Failed to load configs.signatures !" + fi +fi + +config_signature_matches() { + local md5="${1}" file="${2}" + + if [ "${BASH_VERSINFO[0]}" -ge "4" ] + then + [ "${configs_signatures[${md5}]}" = "${file}" ] && return 0 + return 1 + fi + + if [ -f "configs.signatures" ] + then + grep "\['${md5}'\]='${file}'" "configs.signatures" >/dev/null + return $? + fi + + return 1 +} + # backup user configurations installer_backup_suffix="${PID}.${RANDOM}" for x in $(find "${NETDATA_PREFIX}/etc/netdata/" -name '*.conf' -type f) @@ -498,7 +551,8 @@ do if [ -z "${md5sum}" -o ! -x "${md5sum}" ] then # we don't have md5sum - keep it - cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}" + echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' ${TPUT_RET}is not known to distribution${TPUT_RESET}. Keeping it." + run cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}" else # find it relative filename f="${x/*\/etc\/netdata\//}" @@ -512,20 +566,14 @@ do cp "conf.d/${f}" "${x}.orig" fi - if [ "${BASH_VERSINFO[0]}" -ge "4" ] - then - if [ "${configs_signatures[${md5}]}" = "${f}" ] + if config_signature_matches "${md5}" "${f}" then - # it is a stock version - don't keep it - echo >&2 "File '${x}' is stock version." - else - # edited by user - keep it - echo >&2 "File '${x}' has been edited by user." - cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}" - fi + # it is a stock version - don't keep it + echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' is stock version." else - echo >&2 "File '${x}' cannot be check for custom edits." - cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}" + # edited by user - keep it + echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' ${TPUT_RED} has been edited by user${TPUT_RESET}. Keeping it." + run cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}" fi fi @@ -535,194 +583,79 @@ do fi done -echo >&2 "Installing netdata ..." + +# ----------------------------------------------------------------------------- +progress "Install netdata" + run make install || exit 1 -# restore user configurations + +# ----------------------------------------------------------------------------- +progress "Restore user edited netdata configuration files" + for x in $(find "${NETDATA_PREFIX}/etc/netdata/" -name '*.conf' -type f) do if [ -f "${x}.installer_backup.${installer_backup_suffix}" ] then - cp -p "${x}.installer_backup.${installer_backup_suffix}" "${x}" - rm -f "${x}.installer_backup.${installer_backup_suffix}" + run cp -p "${x}.installer_backup.${installer_backup_suffix}" "${x}" && \ + run rm -f "${x}.installer_backup.${installer_backup_suffix}" fi done -echo >&2 "Fixing permissions ..." - -check_cmd() { - which "${1}" >/dev/null 2>&1 && return 0 - command -v "${1}" >/dev/null 2>&1 && return 0 - return 1 -} - -portable_add_user() { - local username="${1}" - - getent passwd "${username}" > /dev/null 2>&1 - [ $? -eq 0 ] && return 0 - - echo >&2 "Adding ${username} user account ..." - - local nologin="$(which nologin 2>/dev/null || command -v nologin 2>/dev/null || echo '/bin/false')" - - # Linux - if check_cmd useradd - then - run useradd -r -g "${username}" -c "${username}" -s "${nologin}" -d / "${username}" && return 0 - fi - - # FreeBSD - if check_cmd pw - then - run pw useradd "${username}" -d / -g "${username}" -s "${nologin}" && return 0 - fi - - # BusyBox - if check_cmd adduser - then - run adduser -D -G "${username}" "${username}" && return 0 - fi - - echo >&2 "Failed to add ${username} user account !" - - return 1 -} - -portable_add_group() { - local groupname="${1}" - - getent group "${groupname}" > /dev/null 2>&1 - [ $? -eq 0 ] && return 0 - - echo >&2 "Adding ${groupname} user group ..." - - # Linux - if check_cmd groupadd - then - run groupadd -r "${groupname}" && return 0 - fi - - # FreeBSD - if check_cmd pw - then - run pw groupadd "${groupname}" && return 0 - fi - - # BusyBox - if check_cmd addgroup - then - run addgroup "${groupname}" && return 0 - fi - - echo >&2 "Failed to add ${groupname} user group !" - return 1 -} - -portable_add_user_to_group() { - local groupname="${1}" username="${2}" - - getent group "${groupname}" > /dev/null 2>&1 - [ $? -ne 0 ] && return 1 - - # find the user is already in the group - local users=$(getent group "${groupname}" | cut -d ':' -f 4) - if [[ ",${users}," =~ ,${username}, ]] - then - # username is already there - return 0 - else - # username is not in group - echo >&2 "Adding ${username} user to the ${groupname} group ..." - - # Linux - if check_cmd usermod - then - run usermod -a -G "${groupname}" "${username}" && return 0 - fi - - # FreeBSD - if check_cmd pw - then - run pw groupmod "${groupname}" -m "${username}" && return 0 - fi - - # BusyBox - if check_cmd addgroup - then - run addgroup "${username}" "${groupname}" && return 0 - fi - echo >&2 "Failed to add user ${username} to group ${groupname} !" - return 1 - fi -} - -iscontainer() { - # man systemd-detect-virt - local cmd=$(which systemd-detect-virt 2>/dev/null || command -v systemd-detect-virt 2>/dev/null) - if [ ! -z "${cmd}" -a -x "${cmd}" ] - then - "${cmd}" --container >/dev/null 2>&1 && return 0 - fi - - # /proc/1/sched exposes the host's pid of our init ! - # http://stackoverflow.com/a/37016302 - local pid=$( cat /proc/1/sched | head -n 1 | { IFS='(),#:' read name pid th threads; echo $pid; } ) - local p=$(( pid + 0 )) - [ ${pid} -ne 1 ] && return 0 - - # lxc sets environment variable 'container' - [ ! -z "${container}" ] && return 0 - - # docker creates /.dockerenv - # http://stackoverflow.com/a/25518345 - [ -f "/.dockerenv" ] && return 0 +# ----------------------------------------------------------------------------- +progress "Fix generated files permissions" - # ubuntu and debian supply /bin/running-in-container - # https://www.apt-browse.org/browse/ubuntu/trusty/main/i386/upstart/1.12.1-0ubuntu4/file/bin/running-in-container - if [ -x "/bin/running-in-container" ] - then - "/bin/running-in-container" >/dev/null 2>&1 && return 0 - fi +run find ./system/ -type f -a \! -name \*.in -a \! -name Makefile\* -a \! -name \*.conf -a \! -name \*.service -a \! -name \*.logrotate -exec chmod 755 {} \; - return 1 -} -run find ./system/ -type f -a \! -name \*.in -a \! -name Makefile\* -a \! -name \*.conf -a \! -name \*.service -a \! -name \*.logrotate -exec chmod 755 {} \; +# ----------------------------------------------------------------------------- +progress "Add user netdata to required user groups" NETDATA_ADDED_TO_DOCKER=0 NETDATA_ADDED_TO_NGINX=0 NETDATA_ADDED_TO_VARNISH=0 NETDATA_ADDED_TO_HAPROXY=0 +NETDATA_ADDED_TO_ADM=0 +NETDATA_ADDED_TO_NSD=0 if [ ${UID} -eq 0 ] then portable_add_group netdata portable_add_user netdata - portable_add_user_to_group docker netdata && NETDATA_ADDED_TO_DOCKER=1 - portable_add_user_to_group nginx netdata && NETDATA_ADDED_TO_NGINX=1 + portable_add_user_to_group docker netdata && NETDATA_ADDED_TO_DOCKER=1 + portable_add_user_to_group nginx netdata && NETDATA_ADDED_TO_NGINX=1 portable_add_user_to_group varnish netdata && NETDATA_ADDED_TO_VARNISH=1 portable_add_user_to_group haproxy netdata && NETDATA_ADDED_TO_HAPROXY=1 + portable_add_user_to_group adm netdata && NETDATA_ADDED_TO_ADM=1 + portable_add_user_to_group nsd netdata && NETDATA_ADDED_TO_NSD=1 + run_ok +else + run_failed "The installer does not run as root." +fi + +# ----------------------------------------------------------------------------- +progress "Install logrotate configuration for netdata" +if [ ${UID} -eq 0 ] + then if [ -d /etc/logrotate.d -a ! -f /etc/logrotate.d/netdata ] then - echo >&2 "Adding netdata logrotate configuration ..." run cp system/netdata.logrotate /etc/logrotate.d/netdata fi if [ -f /etc/logrotate.d/netdata ] then - echo >&2 "Fixing netdata logrotate permissions ..." run chmod 644 /etc/logrotate.d/netdata fi fi # ----------------------------------------------------------------------------- -# load options from the configuration file +progress "Read installation options from netdata.conf" # create an empty config if it does not exist -[ ! -f "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ] && touch "${NETDATA_PREFIX}/etc/netdata/netdata.conf" +[ ! -f "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ] && \ + touch "${NETDATA_PREFIX}/etc/netdata/netdata.conf" # function to extract values from the config file config_option() { @@ -775,17 +708,14 @@ NETDATA_RUN_DIR="${NETDATA_PREFIX}/var/run" # ----------------------------------------------------------------------------- -# prepare the directories +progress "Fix permissions of netdata directories (using user '${NETDATA_USER}')" -# this is needed if NETDATA_PREFIX is not empty if [ ! -d "${NETDATA_RUN_DIR}" ] then - mkdir -p "${NETDATA_RUN_DIR}" || exit 1 + # this is needed if NETDATA_PREFIX is not empty + run mkdir -p "${NETDATA_RUN_DIR}" || exit 1 fi -echo >&2 -echo >&2 "Fixing directories (user: ${NETDATA_USER})..." - # --- conf dir ---- for x in "python.d" "charts.d" "node.d" @@ -832,7 +762,13 @@ run chmod 755 "${NETDATA_LOG_DIR}" if [ ${UID} -eq 0 ] then - run chown "${NETDATA_USER}:root" "${NETDATA_LOG_DIR}" + # find the admin group + admin_group= + test -z "${admin_group}" && getent group root >/dev/null 2>&1 && admin_group="root" + test -z "${admin_group}" && getent group daemon >/dev/null 2>&1 && admin_group="daemon" + test -z "${admin_group}" && admin_group="${NETDATA_USER}" + + run chown "${NETDATA_USER}:${admin_group}" "${NETDATA_LOG_DIR}" run chown -R root "${NETDATA_PREFIX}/usr/libexec/netdata" run find "${NETDATA_PREFIX}/usr/libexec/netdata" -type d -exec chmod 0755 {} \; run find "${NETDATA_PREFIX}/usr/libexec/netdata" -type f -exec chmod 0644 {} \; @@ -842,15 +778,18 @@ if [ ${UID} -eq 0 ] setcap_ret=1 if ! iscontainer then - run setcap cap_dac_read_search,cap_sys_ptrace+ep "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" - setcap_ret=$? + if [ ! -z "${setcap}" ] + then + run setcap cap_dac_read_search,cap_sys_ptrace+ep "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" + setcap_ret=$? + fi if [ ${setcap_ret} -eq 0 ] then # if we managed to setcap # but we fail to execute apps.plugin # trigger setuid to root - "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" -v >/dev/null 2>&1 + "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" -t >/dev/null 2>&1 setcap_ret=$? fi fi @@ -861,6 +800,13 @@ if [ ${UID} -eq 0 ] run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" run chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" fi + + if [ -f "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/freeipmi.plugin" ] + then + run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/freeipmi.plugin" + run chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/freeipmi.plugin" + fi + else run chown "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_LOG_DIR}" run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_PREFIX}/usr/libexec/netdata" @@ -870,33 +816,82 @@ fi # --- fix #1292 bug --- -[ -d "${NETDATA_PREFIX}/usr/libexec" ] && run chmod a+rX "${NETDATA_PREFIX}/usr/libexec" +[ -d "${NETDATA_PREFIX}/usr/libexec" ] && run chmod a+rX "${NETDATA_PREFIX}/usr/libexec" [ -d "${NETDATA_PREFIX}/usr/share/netdata" ] && run chmod a+rX "${NETDATA_PREFIX}/usr/share/netdata" + # ----------------------------------------------------------------------------- -# check if we can re-start netdata +progress "Install netdata at system init" -if [ ${DONOTSTART} -eq 1 ] - then - if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ] +installed_init_d=0 +install_non_systemd_init() { + [ "${UID}" != 0 ] && return 1 + + local key="unknown" + if [ -f /etc/os-release ] then - echo >&2 "Generating empty config file in: ${NETDATA_PREFIX}/etc/netdata/netdata.conf" - echo "# Get config from http://127.0.0.1:${NETDATA_PORT}/netdata.conf" >"${NETDATA_PREFIX}/etc/netdata/netdata.conf" + source /etc/os-release || return 1 + key="${ID}-${VERSION_ID}" - if [ "${UID}" -eq 0 ] + elif [ -f /etc/centos-release ] + then + key=$(&2 " enjoy real-time performance and health monitoring..." - exit 0 + + return 0 +} + +if [ "${UID}" -eq 0 ] + then + + if issystemd + then + # systemd is running on this system + + if [ ! -f /etc/systemd/system/netdata.service ] + then + echo >&2 "Installing systemd service..." + run cp system/netdata.service /etc/systemd/system/netdata.service && \ + run systemctl daemon-reload && \ + run systemctl enable netdata + fi + else + install_non_systemd_init + fi fi + # ----------------------------------------------------------------------------- -# stop a running netdata +# check if we can re-start netdata + +started=0 isnetdata() { if [ -d /proc/self ] @@ -946,7 +941,7 @@ stop_all_netdata() { myns="$(readlink /proc/self/ns/pid 2>/dev/null)" - echo >&2 "Stopping a (possibly) running netdata..." + # echo >&2 "Stopping a (possibly) running netdata (namespace '${myns}')..." for p in $(cat "${NETDATA_RUN_DIR}/netdata.pid" 2>/dev/null) \ $(cat /var/run/netdata.pid 2>/dev/null) \ @@ -962,111 +957,25 @@ stop_all_netdata() { done } -# ----------------------------------------------------------------------------- -# check for systemd - -issystemd() { - local pids p myns ns systemctl - - # if the directory /etc/systemd/system does not exit, it is not systemd - [ ! -d /etc/systemd/system ] && return 1 - - # if there is no systemctl command, it is not systemd - systemctl=$(which systemctl 2>/dev/null || command -v systemctl 2>/dev/null) - [ -z "${systemctl}" -o ! -x "${systemctl}" ] && return 1 - - # if pid 1 is systemd, it is systemd - [ "$(basename $(readlink /proc/1/exe) 2>/dev/null)" = "systemd" ] && return 0 - - # if systemd is not running, it is not systemd - pids=$(pidof systemd 2>/dev/null) - [ -z "${pids}" ] && return 1 - - # check if the running systemd processes are not in our namespace - myns="$(readlink /proc/self/ns/pid 2>/dev/null)" - for p in ${pids} - do - ns="$(readlink /proc/${p}/ns/pid 2>/dev/null)" - - # if pid of systemd is in our namespace, it is systemd - [ ! -z "${myns}" && "${myns}" = "${ns}" ] && return 0 - done - - # else, it is not systemd - return 1 -} - -installed_init_d=0 -install_non_systemd_init() { - [ "${UID}" != 0 ] && return 1 - - local key="unknown" - if [ -f /etc/os-release ] - then - source /etc/os-release || return 1 - key="${ID}-${VERSION_ID}" - - elif [ -f /etc/centos-release ] - then - key=$("${NETDATA_PREFIX}/etc/netdata/netdata.conf" - elif [ "${key}" = "CentOS release 6.8 (Final)" -o "${key}" = "amzn-2016.09" ] + if [ "${UID}" -eq 0 ] then - run cp system/netdata-init-d /etc/init.d/netdata && \ - run chmod 755 /etc/init.d/netdata && \ - run chkconfig netdata on && \ - installed_init_d=1 + chown "${NETDATA_USER}" "${NETDATA_PREFIX}/etc/netdata/netdata.conf" fi + chmod 0644 "${NETDATA_PREFIX}/etc/netdata/netdata.conf" fi - return 0 -} - -started=0 -if [ "${UID}" -eq 0 ] - then +else - if issystemd - then - # systemd is running on this system + progress "Start netdata" - if [ ! -f /etc/systemd/system/netdata.service ] + if [ "${UID}" -eq 0 ] then - echo >&2 "Installing systemd service..." - run cp system/netdata.service /etc/systemd/system/netdata.service && \ - run systemctl daemon-reload && \ - run systemctl enable netdata - else - service netdata stop - fi - - stop_all_netdata - service netdata restart && started=1 - else - install_non_systemd_init - fi - - if [ ${started} -eq 0 ] - then - # check if we can use the system service service netdata stop stop_all_netdata service netdata restart && started=1 @@ -1075,89 +984,88 @@ if [ "${UID}" -eq 0 ] service netdata start && started=1 fi fi -fi -if [ ${started} -eq 0 ] -then - # still not started... + if [ ${started} -eq 0 ] + then + # still not started... - stop_all_netdata + stop_all_netdata + + echo >&2 "Starting netdata..." + run ${NETDATA_PREFIX}/usr/sbin/netdata -P ${NETDATA_RUN_DIR}/netdata.pid "${@}" + if [ $? -ne 0 ] + then + echo >&2 + echo >&2 "SORRY! FAILED TO START NETDATA!" + exit 1 + else + echo >&2 "OK. NetData Started!" + fi - echo >&2 "Starting netdata..." - run ${NETDATA_PREFIX}/usr/sbin/netdata -P ${NETDATA_RUN_DIR}/netdata.pid "${@}" - if [ $? -ne 0 ] - then echo >&2 - echo >&2 "SORRY! FAILED TO START NETDATA!" - exit 1 - else - echo >&2 "OK. NetData Started!" fi - echo >&2 -fi - -# ----------------------------------------------------------------------------- -# save a config file, if it is not already there - -if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ] - then - echo >&2 - echo >&2 "-------------------------------------------------------------------------------" - echo >&2 - echo >&2 "Downloading default configuration from netdata..." - sleep 5 + # ----------------------------------------------------------------------------- + # save a config file, if it is not already there - # remove a possibly obsolete download - [ -f "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] && rm "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" + if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ] + then + echo >&2 + echo >&2 "-------------------------------------------------------------------------------" + echo >&2 + echo >&2 "Downloading default configuration from netdata..." + sleep 5 - # disable a proxy to get data from the local netdata - export http_proxy= - export https_proxy= + # remove a possibly obsolete download + [ -f "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] && rm "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" - # try wget - wget 2>/dev/null -O "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "http://localhost:${NETDATA_PORT}/netdata.conf" - ret=$? + # disable a proxy to get data from the local netdata + export http_proxy= + export https_proxy= - # try curl - if [ ${ret} -ne 0 -o ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] - then - curl -s -o "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "http://localhost:${NETDATA_PORT}/netdata.conf" + # try wget + wget 2>/dev/null -O "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "http://localhost:${NETDATA_PORT}/netdata.conf" ret=$? - fi - if [ ${ret} -eq 0 -a -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] - then - mv "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "${NETDATA_PREFIX}/etc/netdata/netdata.conf" - echo >&2 "New configuration saved for you to edit at ${NETDATA_PREFIX}/etc/netdata/netdata.conf" + # try curl + if [ ${ret} -ne 0 -o ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] + then + curl -s -o "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "http://localhost:${NETDATA_PORT}/netdata.conf" + ret=$? + fi - if [ "${UID}" -eq 0 ] + if [ ${ret} -eq 0 -a -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] then - chown "${NETDATA_USER}" "${NETDATA_PREFIX}/etc/netdata/netdata.conf" + mv "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "${NETDATA_PREFIX}/etc/netdata/netdata.conf" + echo >&2 "New configuration saved for you to edit at ${NETDATA_PREFIX}/etc/netdata/netdata.conf" + + if [ "${UID}" -eq 0 ] + then + chown "${NETDATA_USER}" "${NETDATA_PREFIX}/etc/netdata/netdata.conf" + fi + chmod 0664 "${NETDATA_PREFIX}/etc/netdata/netdata.conf" + else + echo >&2 "Cannnot download configuration from netdata daemon using url 'http://localhost:${NETDATA_PORT}/netdata.conf'" + [ -f "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] && rm "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" fi - chmod 0664 "${NETDATA_PREFIX}/etc/netdata/netdata.conf" - else - echo >&2 "Cannnot download configuration from netdata daemon using url 'http://localhost:${NETDATA_PORT}/netdata.conf'" - [ -f "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] && rm "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" fi fi # ----------------------------------------------------------------------------- -# Check for KSM +progress "Check KSM (kernel memory deduper)" ksm_is_available_but_disabled() { cat </sys/kernel/mm/ksm/run -echo 1000 >/sys/kernel/mm/ksm/sleep_millisecs + ${TPUT_YELLOW}${TPUT_BOLD}echo 1 >/sys/kernel/mm/ksm/run${TPUT_RESET} + ${TPUT_YELLOW}${TPUT_BOLD}echo 1000 >/sys/kernel/mm/ksm/sleep_millisecs${TPUT_RESET} If you enable it, you will save 40-60% of netdata memory. @@ -1167,8 +1075,7 @@ KSM1 ksm_is_not_available() { cat <netdata-uninstaller.sh < /dev/null +if [ $? -eq 0 -a "${NETDATA_ADDED_TO_ADM}" = "1" ] + then + echo + echo "You may also want to remove the netdata user from the adm group" + echo "by running:" + echo " gpasswd -d netdata adm" +fi + +getent group nsd > /dev/null +if [ $? -eq 0 -a "${NETDATA_ADDED_TO_NSD}" = "1" ] + then + echo + echo "You may also want to remove the netdata user from the nsd group" + echo "by running:" + echo " gpasswd -d netdata nsd" +fi + UNINSTALL chmod 750 netdata-uninstaller.sh # ----------------------------------------------------------------------------- +progress "Basic netdata instructions" cat <&2 "Uninstall script generated: ./netdata-uninstaller.sh" +echo >&2 "Uninstall script generated: ${TPUT_RED}${TPUT_BOLD}./netdata-uninstaller.sh${TPUT_RESET}" if [ -d .git ] then @@ -1408,6 +1325,7 @@ force=0 export PATH="\${PATH}:${PATH}" export CFLAGS="${CFLAGS}" +export NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS}" INSTALL_UID="${UID}" if [ "\${INSTALL_UID}" != "\${UID}" ] @@ -1436,7 +1354,7 @@ if [ -t 2 ] else # we are headless # create a temporary file for the log - tmp=\$(mktemp /tmp/netdata-updater-log-XXXXXX.log) + tmp=\$(mktemp /tmp/netdata-updater.log.XXXXXX) # open fd 3 and send it to tmp exec 3>\${tmp} fi @@ -1512,13 +1430,40 @@ update && exit 0 REINSTALL chmod 755 netdata-updater.sh.new mv -f netdata-updater.sh.new netdata-updater.sh - echo >&2 "Update script generated : ./netdata-updater.sh" + echo >&2 "Update script generated : ${TPUT_GREEN}${TPUT_BOLD}./netdata-updater.sh${TPUT_RESET}" + echo >&2 + echo >&2 "${TPUT_DIM}${TPUT_BOLD}netdata-updater.sh${TPUT_RESET}${TPUT_DIM} can work from cron. It will trigger an email from cron" + echo >&2 "only if it fails (it does not print anything if it can update netdata).${TPUT_RESET}" + if [ "${UID}" -eq "0" ] + then + if [ -d "/etc/cron.daily" -a ! -f "/etc/cron.daily/netdata-updater.sh" ] + then + echo >&2 "${TPUT_DIM}Run this to automatically check and install netdata updates once per day:${TPUT_RESET}" + echo >&2 + echo >&2 "${TPUT_YELLOW}${TPUT_BOLD}ln -s ${PWD}/netdata-updater.sh /etc/cron.daily/netdata-updater.sh${TPUT_RESET}" + elif [ -d "/etc/periodic/daily" -a ! -f "/etc/periodic/daily/netdata-updater" ] + then + echo >&2 "${TPUT_DIM}Run this to automatically check and install netdata updates once per day:${TPUT_RESET}" + echo >&2 + echo >&2 "${TPUT_YELLOW}${TPUT_BOLD}ln -s ${PWD}/netdata-updater.sh /etc/periodic/daily/netdata-updater${TPUT_RESET}" + fi + fi elif [ -f "netdata-updater.sh" ] then rm "netdata-updater.sh" fi -banner "is installed and running now!" +# ----------------------------------------------------------------------------- +echo >&2 +progress "We are done!" + +if [ ${started} -eq 1 ] + then + netdata_banner "is installed and running now!" +else + netdata_banner "is installed now!" +fi + echo >&2 " enjoy real-time performance and health monitoring..." echo >&2 exit 0 diff --git a/netdata.spec b/netdata.spec index 1d0988579..6a3d63f87 100644 --- a/netdata.spec +++ b/netdata.spec @@ -1,20 +1,9 @@ -%define contentdir %{_datadir}/netdata -%if 0%{?suse_version} -%define distro_post %service_add_post netdata.service -%define distro_preun %service_del_preun netdata.service -%define distro_postun %service_del_postun netdata.service -%define distro_buildrequires BuildRequires:\ systemd-rpm-macros -%else -%define distro_post %systemd_post netdata.service -%define distro_preun %systemd_preun netdata.service -%define distro_postun %systemd_postun_with_restart netdata.service -%define distro_buildrequires %{nil} -%endif +%global contentdir %{_datadir}/netdata # This is temporary and should eventually be resolved. This bypasses # the default rhel __os_install_post which throws a python compile # error. -%define __os_install_post %{nil} +%global __os_install_post %{nil} # # Conditional build: @@ -26,33 +15,79 @@ %undefine with_systemd %endif +%if %{with systemd} +%if 0%{?suse_version} +%global netdata_initd_buildrequires \ +BuildRequires: systemd-rpm-macros \ +%{nil} +%global netdata_initd_requires \ +%{?systemd_requires} \ +%{nil} +%global netdata_init_post %service_add_post netdata.service +%global netdata_init_preun %service_del_preun netdata.service +%global netdata_init_postun %service_del_postun netdata.service +%else +%global netdata_initd_buildrequires %{nil} +%global netdata_initd_requires \ +Requires(preun): systemd-units \ +Requires(postun): systemd-units \ +Requires(post): systemd-units \ +%{nil} +%global netdata_init_post %systemd_post netdata.service +%global netdata_init_preun %systemd_preun netdata.service +%global netdata_init_postun %systemd_postun_with_restart netdata.service +%endif +%else +%global netdata_initd_buildrequires %{nil} +%global netdata_initd_requires \ +Requires(post): chkconfig \ +%{nil} +%global netdata_init_post \ +/sbin/chkconfig --add netdata \ +%{nil} +%global netdata_init_preun %{nil} \ +if [ $1 = 0 ]; then \ + /sbin/service netdata stop > /dev/null 2>&1 \ + /sbin/chkconfig --del netdata \ +fi \ +%{nil} +%global netdata_init_postun %{nil} \ +if [ $1 != 0 ]; then \ + /sbin/service netdata condrestart 2>&1 > /dev/null \ +fi \ +%{nil} +%endif + +%if 0%{?_fedora} +%global netdata_recommends \ +Recommends: curl \ +Recommends: iproute-tc \ +Recommends: lm_sensors \ +Recommends: nmap-ncat \ +Recommends: nodejs \ +Recommends: python \ +Recommends: PyYAML \ +Recommends: python2-PyMySQL \ +Recommends: python2-psycopg2 \ +%{nil} +%else +%global netdata_recommends %{nil} +%endif + Summary: Real-time performance monitoring, done right Name: netdata -Version: 1.5.0 +Version: 1.6.0 Release: 1%{?dist} -License: GPL v3+ +License: GPLv3+ Group: Applications/System -Source0: http://firehol.org/download/netdata/releases/v1.5.0/%{name}-1.5.0.tar.xz -URL: http://my-netdata.io/ -%distro_buildrequires -BuildRequires: /usr/bin/autoconf -BuildRequires: /usr/bin/automake +Source0: https://firehol.org/download/netdata/releases/v1.6.0/%{name}-1.6.0.tar.xz +URL: http://my-netdata.io BuildRequires: pkgconfig BuildRequires: xz BuildRequires: zlib-devel BuildRequires: libuuid-devel Requires: zlib Requires: libuuid -Requires(post): libcap -Recommends: curl -Recommends: iproute-tc -Recommends: lm_sensors -Recommends: nmap-ncat -Recommends: nodejs -Recommends: python -Recommends: PyYAML -Recommends: python2-PyMySQL -Recommends: python2-psycopg2 # Packages can be found in the EPEL repo %if %{with nfacct} @@ -64,18 +99,11 @@ Requires: libnetfilter_acct Requires(pre): /usr/sbin/groupadd Requires(pre): /usr/sbin/useradd +Requires(post): libcap -%if %{with systemd} -%if 0%{?suse_version} -%{?systemd_requires} -%else -Requires(preun): systemd-units -Requires(postun): systemd-units -Requires(post): systemd-units -%endif -%else -Requires(post): chkconfig -%endif +%{netdata_initd_buildrequires} +%{netdata_recommends} +%{netdata_initd_requires} %description netdata is the fastest way to visualize metrics. It is a resource @@ -88,10 +116,9 @@ so that you can get insights of what is happening now and what just happened, on your systems and applications. %prep -%setup -q +%setup -q -n netdata-1.6.0 %build -./autogen.sh %configure \ --with-zlib \ --with-math \ @@ -100,80 +127,43 @@ happened, on your systems and applications. %{__make} %{?_smp_mflags} %install -rm -rf $RPM_BUILD_ROOT -%{__make} %{?_smp_mflags} DESTDIR=$RPM_BUILD_ROOT install +rm -rf "${RPM_BUILD_ROOT}" +%{__make} %{?_smp_mflags} DESTDIR="${RPM_BUILD_ROOT}" install -find $RPM_BUILD_ROOT -name .keep -delete +find "${RPM_BUILD_ROOT}" -name .keep -delete -install -m 644 -p system/netdata.conf $RPM_BUILD_ROOT%{_sysconfdir}/%{name} -install -d $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d -install -m 644 -p system/netdata.logrotate $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/%{name} +install -m 644 -p system/netdata.conf "${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}" +install -m 755 -d "${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d" +install -m 644 -p system/netdata.logrotate "${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}" %if %{with systemd} -install -d $RPM_BUILD_ROOT%{_unitdir} -install -m 644 -p system/netdata.service $RPM_BUILD_ROOT%{_unitdir}/netdata.service +install -m 755 -d "${RPM_BUILD_ROOT}%{_unitdir}" +install -m 644 -p system/netdata.service "${RPM_BUILD_ROOT}%{_unitdir}/netdata.service" %else # install SYSV init stuff -install -d $RPM_BUILD_ROOT/etc/rc.d/init.d -install -m755 system/netdata-init-d \ - $RPM_BUILD_ROOT/etc/rc.d/init.d/netdata +install -d "${RPM_BUILD_ROOT}/etc/rc.d/init.d" +install -m 755 system/netdata-init-d \ + "${RPM_BUILD_ROOT}/etc/rc.d/init.d/netdata" %endif -%if %{with systemd} %pre -# Add the "netdata" user -/usr/sbin/groupadd -r netdata 2> /dev/null || : -/usr/sbin/useradd -c "netdata" -g netdata \ - -s /sbin/nologin -r -d %{contentdir} netdata 2> /dev/null || : - -%post -%distro_post -setcap cap_dac_read,cap_sys_ptrace+ep /usr/libexec/netdata/plugins.d/apps.plugin || chmod 1755 /usr/libexec/netdata/plugins.d/apps.plugin - -%preun -%distro_preun - -%postun -%distro_postun -%else -%pre -# Add the "netdata" user getent group netdata >/dev/null || groupadd -r netdata getent group docker >/dev/null || groupadd -r docker getent passwd netdata >/dev/null || \ useradd -r -g netdata -G docker -s /sbin/nologin \ -d %{contentdir} -c "netdata" netdata -exit 0 %post -setcap cap_dac_read,cap_sys_ptrace+ep /usr/libexec/netdata/plugins.d/apps.plugin || chmod 1755 /usr/libexec/netdata/plugins.d/apps.plugin -# Register the netdata service -/sbin/chkconfig --add netdata -# Only gets run on initial install (not upgrades or uninstalls) -if [ $1 = 1 ]; then - # Start the netdata service - /sbin/service netdata start -fi -exit 0 +%{netdata_init_post} %preun -# Only gets run on uninstall (not upgrades) -if [ $1 = 0 ]; then - /sbin/service netdata stop > /dev/null 2>&1 - /sbin/chkconfig --del netdata -fi -exit 0 +%{netdata_init_preun} %postun -# Only gets run on upgrade (not uninstalls) -if [ $1 != 0 ]; then - /sbin/service netdata condrestart 2>&1 > /dev/null -fi -exit 0 -%endif +%{netdata_init_postun} %clean -rm -rf $RPM_BUILD_ROOT +rm -rf "${RPM_BUILD_ROOT}" %files %doc README.md @@ -190,12 +180,11 @@ rm -rf $RPM_BUILD_ROOT # To be eventually moved to %%_defaultdocdir %{_sysconfdir}/%{name}/node.d/*.md - -%caps(cap_dac_read_search,cap_sys_ptrace=ep) %{_libexecdir}/%{name}/plugins.d/apps.plugin - %{_libexecdir}/%{name} %{_sbindir}/%{name} +%caps(cap_dac_read_search,cap_sys_ptrace=ep) %attr(0555,root,root) %{_libexecdir}/%{name}/plugins.d/apps.plugin + %attr(0700,netdata,netdata) %dir %{_localstatedir}/cache/%{name} %attr(0700,netdata,netdata) %dir %{_localstatedir}/log/%{name} %attr(0700,netdata,netdata) %dir %{_localstatedir}/lib/%{name} @@ -216,6 +205,17 @@ rm -rf $RPM_BUILD_ROOT %{_datadir}/%{name}/web %changelog +* Mon Mar 20 2017 Costa Tsaousis - 1.6.0-1 +- central netdata +- monitoring ephemeral nodes +- monitoring ephemeral containers and VM guests +- apps.plugin ported for FreeBSD +- web_log plugin +- JSON backends +- IPMI monitoring +- several new and improved plugins +- several new and improved alarms and notifications +- dozens more improvements and bug fixes * Sun Jan 22 2017 Costa Tsaousis - 1.5.0-1 - FreeBSD, MacOS, FreeNAS - Backends support diff --git a/netdata.spec.in b/netdata.spec.in index 2fc4e99e4..685d6e0e7 100644 --- a/netdata.spec.in +++ b/netdata.spec.in @@ -1,20 +1,9 @@ -%define contentdir %{_datadir}/netdata -%if 0%{?suse_version} -%define distro_post %service_add_post netdata.service -%define distro_preun %service_del_preun netdata.service -%define distro_postun %service_del_postun netdata.service -%define distro_buildrequires BuildRequires:\ systemd-rpm-macros -%else -%define distro_post %systemd_post netdata.service -%define distro_preun %systemd_preun netdata.service -%define distro_postun %systemd_postun_with_restart netdata.service -%define distro_buildrequires %{nil} -%endif +%global contentdir %{_datadir}/netdata # This is temporary and should eventually be resolved. This bypasses # the default rhel __os_install_post which throws a python compile # error. -%define __os_install_post %{nil} +%global __os_install_post %{nil} # # Conditional build: @@ -26,33 +15,79 @@ %undefine with_systemd %endif +%if %{with systemd} +%if 0%{?suse_version} +%global netdata_initd_buildrequires \ +BuildRequires: systemd-rpm-macros \ +%{nil} +%global netdata_initd_requires \ +%{?systemd_requires} \ +%{nil} +%global netdata_init_post %service_add_post netdata.service +%global netdata_init_preun %service_del_preun netdata.service +%global netdata_init_postun %service_del_postun netdata.service +%else +%global netdata_initd_buildrequires %{nil} +%global netdata_initd_requires \ +Requires(preun): systemd-units \ +Requires(postun): systemd-units \ +Requires(post): systemd-units \ +%{nil} +%global netdata_init_post %systemd_post netdata.service +%global netdata_init_preun %systemd_preun netdata.service +%global netdata_init_postun %systemd_postun_with_restart netdata.service +%endif +%else +%global netdata_initd_buildrequires %{nil} +%global netdata_initd_requires \ +Requires(post): chkconfig \ +%{nil} +%global netdata_init_post \ +/sbin/chkconfig --add netdata \ +%{nil} +%global netdata_init_preun %{nil} \ +if [ $1 = 0 ]; then \ + /sbin/service netdata stop > /dev/null 2>&1 \ + /sbin/chkconfig --del netdata \ +fi \ +%{nil} +%global netdata_init_postun %{nil} \ +if [ $1 != 0 ]; then \ + /sbin/service netdata condrestart 2>&1 > /dev/null \ +fi \ +%{nil} +%endif + +%if 0%{?_fedora} +%global netdata_recommends \ +Recommends: curl \ +Recommends: iproute-tc \ +Recommends: lm_sensors \ +Recommends: nmap-ncat \ +Recommends: nodejs \ +Recommends: python \ +Recommends: PyYAML \ +Recommends: python2-PyMySQL \ +Recommends: python2-psycopg2 \ +%{nil} +%else +%global netdata_recommends %{nil} +%endif + Summary: Real-time performance monitoring, done right Name: @PACKAGE_NAME@ Version: @PACKAGE_RPM_VERSION@ Release: 1%{?dist} -License: GPL v3+ +License: GPLv3+ Group: Applications/System -Source0: http://firehol.org/download/netdata/releases/v@PACKAGE_VERSION@/%{name}-@PACKAGE_VERSION@.tar.xz -URL: http://my-netdata.io/ -%distro_buildrequires -BuildRequires: /usr/bin/autoconf -BuildRequires: /usr/bin/automake +Source0: https://firehol.org/download/netdata/releases/v@PACKAGE_VERSION@/%{name}-@PACKAGE_VERSION@.tar.xz +URL: http://my-netdata.io BuildRequires: pkgconfig BuildRequires: xz BuildRequires: zlib-devel BuildRequires: libuuid-devel Requires: zlib Requires: libuuid -Requires(post): libcap -Recommends: curl -Recommends: iproute-tc -Recommends: lm_sensors -Recommends: nmap-ncat -Recommends: nodejs -Recommends: python -Recommends: PyYAML -Recommends: python2-PyMySQL -Recommends: python2-psycopg2 # Packages can be found in the EPEL repo %if %{with nfacct} @@ -64,18 +99,11 @@ Requires: libnetfilter_acct Requires(pre): /usr/sbin/groupadd Requires(pre): /usr/sbin/useradd +Requires(post): libcap -%if %{with systemd} -%if 0%{?suse_version} -%{?systemd_requires} -%else -Requires(preun): systemd-units -Requires(postun): systemd-units -Requires(post): systemd-units -%endif -%else -Requires(post): chkconfig -%endif +%{netdata_initd_buildrequires} +%{netdata_recommends} +%{netdata_initd_requires} %description netdata is the fastest way to visualize metrics. It is a resource @@ -88,10 +116,9 @@ so that you can get insights of what is happening now and what just happened, on your systems and applications. %prep -%setup -q +%setup -q -n @PACKAGE_NAME@-@PACKAGE_VERSION@ %build -./autogen.sh %configure \ --with-zlib \ --with-math \ @@ -100,80 +127,43 @@ happened, on your systems and applications. %{__make} %{?_smp_mflags} %install -rm -rf $RPM_BUILD_ROOT -%{__make} %{?_smp_mflags} DESTDIR=$RPM_BUILD_ROOT install +rm -rf "${RPM_BUILD_ROOT}" +%{__make} %{?_smp_mflags} DESTDIR="${RPM_BUILD_ROOT}" install -find $RPM_BUILD_ROOT -name .keep -delete +find "${RPM_BUILD_ROOT}" -name .keep -delete -install -m 644 -p system/netdata.conf $RPM_BUILD_ROOT%{_sysconfdir}/%{name} -install -d $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d -install -m 644 -p system/netdata.logrotate $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/%{name} +install -m 644 -p system/netdata.conf "${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}" +install -m 755 -d "${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d" +install -m 644 -p system/netdata.logrotate "${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}" %if %{with systemd} -install -d $RPM_BUILD_ROOT%{_unitdir} -install -m 644 -p system/netdata.service $RPM_BUILD_ROOT%{_unitdir}/netdata.service +install -m 755 -d "${RPM_BUILD_ROOT}%{_unitdir}" +install -m 644 -p system/netdata.service "${RPM_BUILD_ROOT}%{_unitdir}/netdata.service" %else # install SYSV init stuff -install -d $RPM_BUILD_ROOT/etc/rc.d/init.d -install -m755 system/netdata-init-d \ - $RPM_BUILD_ROOT/etc/rc.d/init.d/netdata +install -d "${RPM_BUILD_ROOT}/etc/rc.d/init.d" +install -m 755 system/netdata-init-d \ + "${RPM_BUILD_ROOT}/etc/rc.d/init.d/netdata" %endif -%if %{with systemd} %pre -# Add the "netdata" user -/usr/sbin/groupadd -r netdata 2> /dev/null || : -/usr/sbin/useradd -c "netdata" -g netdata \ - -s /sbin/nologin -r -d %{contentdir} netdata 2> /dev/null || : - -%post -%distro_post -setcap cap_dac_read,cap_sys_ptrace+ep /usr/libexec/netdata/plugins.d/apps.plugin || chmod 1755 /usr/libexec/netdata/plugins.d/apps.plugin - -%preun -%distro_preun - -%postun -%distro_postun -%else -%pre -# Add the "netdata" user getent group netdata >/dev/null || groupadd -r netdata getent group docker >/dev/null || groupadd -r docker getent passwd netdata >/dev/null || \ useradd -r -g netdata -G docker -s /sbin/nologin \ -d %{contentdir} -c "netdata" netdata -exit 0 %post -setcap cap_dac_read,cap_sys_ptrace+ep /usr/libexec/netdata/plugins.d/apps.plugin || chmod 1755 /usr/libexec/netdata/plugins.d/apps.plugin -# Register the netdata service -/sbin/chkconfig --add netdata -# Only gets run on initial install (not upgrades or uninstalls) -if [ $1 = 1 ]; then - # Start the netdata service - /sbin/service netdata start -fi -exit 0 +%{netdata_init_post} %preun -# Only gets run on uninstall (not upgrades) -if [ $1 = 0 ]; then - /sbin/service netdata stop > /dev/null 2>&1 - /sbin/chkconfig --del netdata -fi -exit 0 +%{netdata_init_preun} %postun -# Only gets run on upgrade (not uninstalls) -if [ $1 != 0 ]; then - /sbin/service netdata condrestart 2>&1 > /dev/null -fi -exit 0 -%endif +%{netdata_init_postun} %clean -rm -rf $RPM_BUILD_ROOT +rm -rf "${RPM_BUILD_ROOT}" %files %doc README.md @@ -190,12 +180,11 @@ rm -rf $RPM_BUILD_ROOT # To be eventually moved to %%_defaultdocdir %{_sysconfdir}/%{name}/node.d/*.md - -%caps(cap_dac_read_search,cap_sys_ptrace=ep) %{_libexecdir}/%{name}/plugins.d/apps.plugin - %{_libexecdir}/%{name} %{_sbindir}/%{name} +%caps(cap_dac_read_search,cap_sys_ptrace=ep) %attr(0555,root,root) %{_libexecdir}/%{name}/plugins.d/apps.plugin + %attr(0700,netdata,netdata) %dir %{_localstatedir}/cache/%{name} %attr(0700,netdata,netdata) %dir %{_localstatedir}/log/%{name} %attr(0700,netdata,netdata) %dir %{_localstatedir}/lib/%{name} @@ -216,6 +205,17 @@ rm -rf $RPM_BUILD_ROOT %{_datadir}/%{name}/web %changelog +* Mon Mar 20 2017 Costa Tsaousis - 1.6.0-1 +- central netdata +- monitoring ephemeral nodes +- monitoring ephemeral containers and VM guests +- apps.plugin ported for FreeBSD +- web_log plugin +- JSON backends +- IPMI monitoring +- several new and improved plugins +- several new and improved alarms and notifications +- dozens more improvements and bug fixes * Sun Jan 22 2017 Costa Tsaousis - 1.5.0-1 - FreeBSD, MacOS, FreeNAS - Backends support diff --git a/node.d/Makefile.in b/node.d/Makefile.in index 66803fc02..2af9a4a65 100644 --- a/node.d/Makefile.in +++ b/node.d/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. @@ -15,61 +16,6 @@ @SET_MAKE@ 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@ @@ -89,9 +35,12 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = node.d +DIST_COMMON = $(dist_node_DATA) $(dist_nodemodules_DATA) \ + $(dist_nodemodulesber_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 \ @@ -100,32 +49,12 @@ 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_node_DATA) \ - $(dist_nodemodules_DATA) $(dist_nodemodulesber_DATA) \ - $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = -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 = SOURCES = DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -157,12 +86,9 @@ am__installdirs = "$(DESTDIR)$(nodedir)" "$(DESTDIR)$(nodemodulesdir)" \ "$(DESTDIR)$(nodemodulesberdir)" DATA = $(dist_node_DATA) $(dist_nodemodules_DATA) \ $(dist_nodemodulesber_DATA) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -186,7 +112,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@ @@ -200,6 +130,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@ @@ -337,6 +271,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu node.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu node.d/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -356,11 +291,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_nodeDATA: $(dist_node_DATA) @$(NORMAL_INSTALL) + test -z "$(nodedir)" || $(MKDIR_P) "$(DESTDIR)$(nodedir)" @list='$(dist_node_DATA)'; test -n "$(nodedir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(nodedir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(nodedir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -377,11 +309,8 @@ uninstall-dist_nodeDATA: dir='$(DESTDIR)$(nodedir)'; $(am__uninstall_files_from_dir) install-dist_nodemodulesDATA: $(dist_nodemodules_DATA) @$(NORMAL_INSTALL) + test -z "$(nodemodulesdir)" || $(MKDIR_P) "$(DESTDIR)$(nodemodulesdir)" @list='$(dist_nodemodules_DATA)'; test -n "$(nodemodulesdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(nodemodulesdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(nodemodulesdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -398,11 +327,8 @@ uninstall-dist_nodemodulesDATA: dir='$(DESTDIR)$(nodemodulesdir)'; $(am__uninstall_files_from_dir) install-dist_nodemodulesberDATA: $(dist_nodemodulesber_DATA) @$(NORMAL_INSTALL) + test -z "$(nodemodulesberdir)" || $(MKDIR_P) "$(DESTDIR)$(nodemodulesberdir)" @list='$(dist_nodemodulesber_DATA)'; test -n "$(nodemodulesberdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(nodemodulesberdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(nodemodulesberdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -417,11 +343,11 @@ uninstall-dist_nodemodulesberDATA: @list='$(dist_nodemodulesber_DATA)'; test -n "$(nodemodulesberdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(nodemodulesberdir)'; $(am__uninstall_files_from_dir) -tags TAGS: - -ctags CTAGS: +tags: TAGS +TAGS: -cscope cscopelist: +ctags: CTAGS +CTAGS: distdir: $(DISTFILES) @@ -562,23 +488,20 @@ uninstall-am: uninstall-dist_nodeDATA uninstall-dist_nodemodulesDATA \ .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ - ctags-am distclean distclean-generic distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dist_nodeDATA \ - install-dist_nodemodulesDATA install-dist_nodemodulesberDATA \ - 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-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags-am \ - uninstall uninstall-am uninstall-dist_nodeDATA \ +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-dist_nodeDATA install-dist_nodemodulesDATA \ + install-dist_nodemodulesberDATA 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-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am uninstall uninstall-am uninstall-dist_nodeDATA \ uninstall-dist_nodemodulesDATA \ uninstall-dist_nodemodulesberDATA -.PRECIOUS: Makefile - # 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/node.d/snmp.node.js b/node.d/snmp.node.js index 5a478937e..57b37ffa0 100644 --- a/node.d/snmp.node.js +++ b/node.d/snmp.node.js @@ -1,6 +1,7 @@ 'use strict'; - +// netdata snmp module // This program will connect to one or more SNMP Agents +// // example configuration in /etc/netdata/node.d/snmp.conf /* @@ -26,13 +27,15 @@ "oid": ".1.3.6.1.2.1.2.2.1.10.1", "algorithm": "incremental", "multiplier": 8, - "divisor": 1024 + "divisor": 1024, + "offset": 0 }, "out": { "oid": ".1.3.6.1.2.1.2.2.1.16.1", "algorithm": "incremental", "multiplier": -8, - "divisor": 1024 + "divisor": 1024, + "offset": 0 } } }, @@ -46,13 +49,15 @@ "oid": ".1.3.6.1.2.1.2.2.1.10.2", "algorithm": "incremental", "multiplier": 8, - "divisor": 1024 + "divisor": 1024, + "offset": 0 }, "out": { "oid": ".1.3.6.1.2.1.2.2.1.16.2", "algorithm": "incremental", "multiplier": -8, - "divisor": 1024 + "divisor": 1024, + "offset": 0 } } } @@ -89,13 +94,15 @@ "oid": ".1.3.6.1.2.1.2.2.1.10.", "algorithm": "incremental", "multiplier": 8, - "divisor": 1024 + "divisor": 1024, + "offset": 0 }, "out": { "oid": ".1.3.6.1.2.1.2.2.1.16.", "algorithm": "incremental", "multiplier": -8, - "divisor": 1024 + "divisor": 1024, + "offset": 0 } } } @@ -265,7 +272,7 @@ netdata.processors.snmp = { if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + " = " + varbinds[i].value); - if(varbinds[i].type === net_snmp.ObjectType.OctetString) + if(varbinds[i].type === net_snmp.ObjectType.OctetString && service.snmp_oids_index[varbinds[i].oid].type !== 'title') value = parseFloat(varbinds[i].value) * 1000; else value = varbinds[i].value; @@ -360,8 +367,12 @@ var snmp = { for(var j = 0; j < dim_keys_len ; j++) { var d = dim_keys[j]; - if (dimensions[d].value !== null) - service.set(d, dimensions[d].value); + if (dimensions[d].value !== null) { + if(typeof dimensions[d].offset === 'number') + service.set(d, dimensions[d].value + dimensions[d].offset); + else + service.set(d, dimensions[d].value); + } } service.end(); diff --git a/plugins.d/Makefile.in b/plugins.d/Makefile.in index 7e90c9808..2a8806cb1 100644 --- a/plugins.d/Makefile.in +++ b/plugins.d/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,9 +36,11 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = plugins.d +DIST_COMMON = $(dist_plugins_DATA) $(dist_plugins_SCRIPTS) \ + $(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 \ @@ -101,8 +49,6 @@ 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_plugins_SCRIPTS) \ - $(dist_plugins_DATA) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -136,32 +82,12 @@ am__uninstall_files_from_dir = { \ } am__installdirs = "$(DESTDIR)$(pluginsdir)" "$(DESTDIR)$(pluginsdir)" SCRIPTS = $(dist_plugins_SCRIPTS) -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 = SOURCES = DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac DATA = $(dist_plugins_DATA) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -185,7 +111,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@ @@ -199,6 +129,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@ @@ -333,6 +267,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plugins.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu plugins.d/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -352,11 +287,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_pluginsSCRIPTS: $(dist_plugins_SCRIPTS) @$(NORMAL_INSTALL) + test -z "$(pluginsdir)" || $(MKDIR_P) "$(DESTDIR)$(pluginsdir)" @list='$(dist_plugins_SCRIPTS)'; 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 \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ @@ -387,11 +319,8 @@ uninstall-dist_pluginsSCRIPTS: dir='$(DESTDIR)$(pluginsdir)'; $(am__uninstall_files_from_dir) install-dist_pluginsDATA: $(dist_plugins_DATA) @$(NORMAL_INSTALL) + test -z "$(pluginsdir)" || $(MKDIR_P) "$(DESTDIR)$(pluginsdir)" @list='$(dist_plugins_DATA)'; 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 \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -406,11 +335,11 @@ uninstall-dist_pluginsDATA: @list='$(dist_plugins_DATA)'; test -n "$(pluginsdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pluginsdir)'; $(am__uninstall_files_from_dir) -tags TAGS: - -ctags CTAGS: +tags: TAGS +TAGS: -cscope cscopelist: +ctags: CTAGS +CTAGS: distdir: $(DISTFILES) @@ -549,20 +478,18 @@ uninstall-am: uninstall-dist_pluginsDATA uninstall-dist_pluginsSCRIPTS .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ - ctags-am distclean distclean-generic distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dist_pluginsDATA \ - install-dist_pluginsSCRIPTS 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-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ - pdf-am ps ps-am tags-am uninstall uninstall-am \ - uninstall-dist_pluginsDATA uninstall-dist_pluginsSCRIPTS - -.PRECIOUS: Makefile +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-dist_pluginsDATA install-dist_pluginsSCRIPTS \ + 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-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am uninstall-dist_pluginsDATA \ + uninstall-dist_pluginsSCRIPTS # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/plugins.d/alarm-notify.sh b/plugins.d/alarm-notify.sh index d6f3d8b2a..d0188fe3b 100755 --- a/plugins.d/alarm-notify.sh +++ b/plugins.d/alarm-notify.sh @@ -16,6 +16,7 @@ # Supported notification methods: # - emails by @ktsaou # - slack.com notifications by @ktsaou +# - discordapp.com notifications by @lowfive # - pushover.net notifications by @ktsaou # - pushbullet.com push notifications by Tiago Peralta @tperalta82 PR #1070 # - telegram.org notifications by @hashworks PR #1002 @@ -23,7 +24,7 @@ # - kafka notifications by @ktsaou #1342 # - pagerduty.com notifications by Jim Cooley @jimcooley PR #1373 # - messagebird.com notifications by @tech_no_logical #1453 -# - hipchart notifications by @ktsaou #1561 +# - hipchat notifications by @ktsaou #1561 # ----------------------------------------------------------------------------- # testing notifications @@ -46,7 +47,7 @@ then echo >&2 echo >&2 "# SENDING TEST ${x} ALARM TO ROLE: ${recipient}" - "${0}" "${recipient}" "$(hostname)" 1 1 "${id}" "$(date +%s)" "test_alarm" "test.chart" "test.family" "${x}" "${last}" 100 90 "${0}" 1 $((0 + id)) "units" "this is a test alarm to verify notifications work" + "${0}" "${recipient}" "$(hostname)" 1 1 "${id}" "$(date +%s)" "test_alarm" "test.chart" "test.family" "${x}" "${last}" 100 90 "${0}" 1 $((0 + id)) "units" "this is a test alarm to verify notifications work" "new value" "old value" if [ $? -ne 0 ] then echo >&2 "# FAILED" @@ -138,6 +139,15 @@ duration="${15}" # the duration in seconds of the previous alarm state non_clear_duration="${16}" # the total duration in seconds this is/was non-clear units="${17}" # the units of the value info="${18}" # a short description of the alarm +value_string="${19}" # friendly value (with units) +old_value_string="${20}" # friendly old value (with units) + +# ----------------------------------------------------------------------------- +# find a suitable hostname to use, if netdata did not supply a hostname + +[ -z "${host}" ] && host="${NETDATA_HOSTNAME}" +[ -z "${host}" ] && host="${NETDATA_REGISTRY_HOSTNAME}" +[ -z "${host}" ] && host="$(hostname 2>/dev/null)" # ----------------------------------------------------------------------------- # screen statuses we don't need to send a notification @@ -145,14 +155,14 @@ info="${18}" # a short description of the alarm # don't do anything if this is not WARNING, CRITICAL or CLEAR if [ "${status}" != "WARNING" -a "${status}" != "CRITICAL" -a "${status}" != "CLEAR" ] then - info "not sending notification for ${status} on '${chart}.${name}'" + info "not sending notification for ${status} of '${host}.${chart}.${name}'" exit 1 fi # don't do anything if this is CLEAR, but it was not WARNING or CRITICAL if [ "${old_status}" != "WARNING" -a "${old_status}" != "CRITICAL" -a "${status}" = "CLEAR" ] then - info "not sending notification for ${status} on '${chart}.${name}' (last status was ${old_status})" + info "not sending notification for ${status} of '${host}.${chart}.${name}' (last status was ${old_status})" exit 1 fi @@ -172,6 +182,7 @@ sendmail= # enable / disable features SEND_SLACK="YES" +SEND_DISCORD="YES" SEND_PUSHOVER="YES" SEND_TWILIO="YES" SEND_HIPCHAT="YES" @@ -187,6 +198,11 @@ SLACK_WEBHOOK_URL= DEFAULT_RECIPIENT_SLACK= declare -A role_recipients_slack=() +# discord configs +DISCORD_WEBHOOK_URL= +DEFAULT_RECIPIENT_DISCORD= +declare -A role_recipients_discord=() + # pushover configs PUSHOVER_APP_TOKEN= DEFAULT_RECIPIENT_PUSHOVER= @@ -205,6 +221,7 @@ DEFAULT_RECIPIENT_TWILIO= declare -A role_recipients_twilio=() # hipchat configs +HIPCHAT_SERVER= HIPCHAT_AUTH_TOKEN= DEFAULT_RECIPIENT_HIPCHAT= declare -A role_recipients_hipchat=() @@ -283,6 +300,7 @@ filter_recipient_by_criticality() { # find the recipients' addresses per method declare -A arr_slack=() +declare -A arr_discord=() declare -A arr_pushover=() declare -A arr_pushbullet=() declare -A arr_twilio=() @@ -363,6 +381,14 @@ do [ "${r}" != "disabled" ] && filter_recipient_by_criticality slack "${r}" && arr_slack[${r/|*/}]="1" done + # discord + a="${role_recipients_discord[${x}]}" + [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_DISCORD}" + for r in ${a//,/ } + do + [ "${r}" != "disabled" ] && filter_recipient_by_criticality discord "${r}" && arr_discord[${r/|*/}]="1" + done + # pagerduty.com a="${role_recipients_pd[${x}]}" [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_PD}" @@ -376,6 +402,10 @@ done to_slack="${!arr_slack[*]}" [ -z "${to_slack}" ] && SEND_SLACK="NO" +# build the list of discord recipients (channels) +to_discord="${!arr_discord[*]}" +[ -z "${to_discord}" ] && SEND_DISCORD="NO" + # build the list of pushover recipients (user tokens) to_pushover="${!arr_pushover[*]}" [ -z "${to_pushover}" ] && SEND_PUSHOVER="NO" @@ -420,6 +450,9 @@ done # check slack [ -z "${SLACK_WEBHOOK_URL}" ] && SEND_SLACK="NO" +# check discord +[ -z "${DISCORD_WEBHOOK_URL}" ] && SEND_DISCORD="NO" + # check pushover [ -z "${PUSHOVER_APP_TOKEN}" ] && SEND_PUSHOVER="NO" @@ -459,6 +492,7 @@ fi if [ \( \ "${SEND_PUSHOVER}" = "YES" \ -o "${SEND_SLACK}" = "YES" \ + -o "${SEND_DISCORD}" = "YES" \ -o "${SEND_HIPCHAT}" = "YES" \ -o "${SEND_TWILIO}" = "YES" \ -o "${SEND_MESSAGEBIRD}" = "YES" \ @@ -476,6 +510,7 @@ if [ \( \ SEND_PUSHBULLET="NO" SEND_TELEGRAM="NO" SEND_SLACK="NO" + SEND_DISCORD="NO" SEND_TWILIO="NO" SEND_HIPCHAT="NO" SEND_MESSAGEBIRD="NO" @@ -495,6 +530,7 @@ if [ "${SEND_EMAIL}" != "YES" \ -a "${SEND_PUSHOVER}" != "YES" \ -a "${SEND_TELEGRAM}" != "YES" \ -a "${SEND_SLACK}" != "YES" \ + -a "${SEND_DISCORD}" != "YES" \ -a "${SEND_TWILIO}" != "YES" \ -a "${SEND_HIPCHAT}" != "YES" \ -a "${SEND_MESSAGEBIRD}" != "YES" \ @@ -503,16 +539,9 @@ if [ "${SEND_EMAIL}" != "YES" \ -a "${SEND_PD}" != "YES" \ ] then - fatal "All notification methods are disabled. Not sending notification to '${roles}' for '${name}' = '${value}' of chart '${chart}' for status '${status}'." + fatal "All notification methods are disabled. Not sending notification for host '${host}', chart '${chart}' to '${roles}' for '${name}' = '${value}' for status '${status}'." fi -# ----------------------------------------------------------------------------- -# find a suitable hostname to use, if netdata did not supply a hostname - -[ -z "${host}" ] && host="${NETDATA_HOSTNAME}" -[ -z "${host}" ] && host="${NETDATA_REGISTRY_HOSTNAME}" -[ -z "${host}" ] && host="$(hostname 2>/dev/null)" - # ----------------------------------------------------------------------------- # get the date the alarm happened @@ -747,13 +776,13 @@ send_pd() { then for PD_SERVICE_KEY in ${recipients} do - d="${status} ${name}=${value} ${units} - ${host}, ${family}" + d="${status} ${name} = ${value_string} - ${host}, ${family}" ${pd_send} -k ${PD_SERVICE_KEY} \ -t ${t} \ -d "${d}" \ -i ${alarm_id} \ -f 'info'="${info}" \ - -f 'value_w_units'="${value} ${units}" \ + -f 'value_w_units'="${value_string}" \ -f 'when'="${when}" \ -f 'duration'="${duration}" \ -f 'roles'="${roles}" \ @@ -774,10 +803,10 @@ send_pd() { retval=$? if [ ${retval} -eq 0 ] then - info "sent pagerduty.com notification using service key ${PD_SERVICE_KEY::-26}....: ${d}" + info "sent pagerduty.com notification for host ${host} ${chart}.${name} using service key ${PD_SERVICE_KEY::-26}....: ${d}" sent=$((sent + 1)) else - error "failed to send pagerduty.com notification using service key ${PD_SERVICE_KEY::-26}.... (error code ${retval}): ${d}" + error "failed to send pagerduty.com notification for ${host} ${chart}.${name} using service key ${PD_SERVICE_KEY::-26}.... (error code ${retval}): ${d}" fi done @@ -826,16 +855,15 @@ send_twilio() { send_hipchat() { local authtoken="${1}" recipients="${2}" message="${3}" httpcode sent=0 room color sender msg_format notify - if [ "${SEND_HIPCHAT}" = "YES" -a ! -z "${authtoken}" -a ! -z "${recipients}" -a ! -z "${message}" ] - then - + if [ "${SEND_HIPCHAT}" = "YES" -a ! -z "${HIPCHAT_SERVER}" -a ! -z "${authtoken}" -a ! -z "${recipients}" -a ! -z "${message}" ] + then # A label to be shown in addition to the sender's name # Valid length range: 0 - 64. sender="netdata" # Valid values: html, text. # Defaults to 'html'. - msg_format="text" + msg_format="html" # Background color for message. Valid values: yellow, green, red, purple, gray, random. Defaults to 'yellow'. case "${status}" in @@ -856,9 +884,9 @@ send_hipchat() { -H "Content-type: application/json" \ -H "Authorization: Bearer ${authtoken}" \ -d "{\"color\": \"${color}\", \"from\": \"${netdata}\", \"message_format\": \"${msg_format}\", \"message\": \"${message}\", \"notify\": \"${notify}\"}" \ - "https://api.hipchat.com/v2/room/${room}/notification") - - if [ "${httpcode}" == "200" ] + "https://${HIPCHAT_SERVER}/v2/room/${room}/notification") + + if [ "${httpcode}" == "204" ] then info "sent HipChat notification for: ${host} ${chart}.${name} is ${status} to '${room}'" sent=$((sent + 1)) @@ -1008,6 +1036,66 @@ EOF return 1 } +# ----------------------------------------------------------------------------- +# discord sender + +send_discord() { + local webhook="${1}/slack" channels="${2}" httpcode sent=0 channel color payload + + [ "${SEND_DISCORD}" != "YES" ] && return 1 + + case "${status}" in + WARNING) color="warning" ;; + CRITICAL) color="danger" ;; + CLEAR) color="good" ;; + *) color="#777777" ;; + esac + + for channel in ${channels} + do + payload="$(cat <${alarm} ${info_html}
  -${chart}
Chart
 
-${family}
Family
 
-${severity}
Severity
 
-${date}${raised_for_html}
Time
 
-View Netdata
  -The source of this alarm is line ${src} +send_hipchat "${HIPCHAT_AUTH_TOKEN}" "${to_hipchat}" " \ +${host} ${status_message}
\ +${alarm} ${info_html}
\ +${chart} (family ${family})
\ +${date}${raised_for_html}
\ +View netdata dashboard \ +(source of alarm ${src}) \ " SENT_HIPCHAT=$? @@ -1301,6 +1397,7 @@ if [ ${SENT_EMAIL} -eq 0 \ -o ${SENT_PUSHOVER} -eq 0 \ -o ${SENT_TELEGRAM} -eq 0 \ -o ${SENT_SLACK} -eq 0 \ + -o ${SENT_DISCORD} -eq 0 \ -o ${SENT_TWILIO} -eq 0 \ -o ${SENT_HIPCHAT} -eq 0 \ -o ${SENT_MESSAGEBIRD} -eq 0 \ @@ -1315,3 +1412,4 @@ fi # we did not send anything exit 1 + diff --git a/plugins.d/cgroup-name.sh b/plugins.d/cgroup-name.sh index 9bb3bcabb..a1e3abe08 100755 --- a/plugins.d/cgroup-name.sh +++ b/plugins.d/cgroup-name.sh @@ -98,6 +98,8 @@ if [ -z "${NAME}" ] then if [[ "${CGROUP}" =~ ^.*docker[-_/\.][a-fA-F0-9]+[-_\.]?.*$ ]] then + # docker containers + DOCKERID="$( echo "${CGROUP}" | sed "s|^.*docker[-_/]\([a-fA-F0-9]\+\)[-_\.]\?.*$|\1|" )" # echo "DOCKERID=${DOCKERID}" @@ -117,6 +119,11 @@ if [ -z "${NAME}" ] info "docker container '${DOCKERID}' is named '${NAME}'" fi fi + elif [[ "${CGROUP}" =~ machine.slice_machine.*-qemu ]] + then + # libvirtd / qemu virtual machines + + NAME="$(echo ${CGROUP} | sed 's/machine.slice_machine.*-qemu//; s/\/x2d//; s/\/x2d/\-/g; s/\.scope//g')" fi [ -z "${NAME}" ] && NAME="${CGROUP}" diff --git a/plugins.d/fping.plugin b/plugins.d/fping.plugin index d523f4474..232c00630 100755 --- a/plugins.d/fping.plugin +++ b/plugins.d/fping.plugin @@ -22,20 +22,39 @@ if [ "${1}" = "install" ] "${@}" || exit 1 } + download() { + local curl="$(which curl 2>/dev/null || command -v curl 2>/dev/null)" + [ ! -z "${curl}" ] && run curl -s -L "${1}" && return 0 + + local wget="$(which wget 2>/dev/null || command -v wget 2>/dev/null)" + [ ! -z "${wget}" ] && run wget -q -O - "${1}" && return 0 + + echo >&2 "Cannot find 'curl' or 'wget' in this system." && exit 1 + } + [ ! -d /usr/src ] && run mkdir -p /usr/src [ ! -d /usr/local/bin ] && run mkdir -p /usr/local/bin run cd /usr/src - if [ -d fping-ktsaou.git ] + if [ -d fping-3.15 ] then - run cd fping-ktsaou.git - run git pull - else - run git clone https://github.com/ktsaou/fping.git fping-ktsaou.git - run cd fping-ktsaou.git + run rm -rf fping-3.15 || exit 1 fi + download 'https://github.com/schweikert/fping/archive/3.15.tar.gz' | run tar -zxvpf - + [ $? -ne 0 ] && exit 1 + run cd fping-3.15 || exit 1 + + #if [ -d fping-ktsaou.git ] + # then + # run cd fping-ktsaou.git + # run git pull + #else + # run git clone https://github.com/ktsaou/fping.git fping-ktsaou.git + # run cd fping-ktsaou.git + #fi + run ./autogen.sh run ./configure --prefix=/usr/local run make clean diff --git a/plugins.d/python.d.plugin b/plugins.d/python.d.plugin index b4e6473a6..efa62cbc5 100755 --- a/plugins.d/python.d.plugin +++ b/plugins.d/python.d.plugin @@ -67,6 +67,34 @@ try: except ImportError: msg.fatal('Cannot find yaml library') +try: + from collections import OrderedDict + ORDERED = True + DICT = OrderedDict + msg.info('YAML output is ordered') +except ImportError: + try: + from ordereddict import OrderedDict + ORDERED = True + DICT = OrderedDict + msg.info('YAML output is ordered') + except ImportError: + ORDERED = False + DICT = dict + msg.info('YAML output is unordered') +if ORDERED: + def ordered_load(stream, Loader=yaml.Loader, object_pairs_hook=OrderedDict): + class OrderedLoader(Loader): + pass + + def construct_mapping(loader, node): + loader.flatten_mapping(node) + return object_pairs_hook(loader.construct_pairs(node)) + OrderedLoader.add_constructor( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, + construct_mapping) + return yaml.load(stream, OrderedLoader) + class PythonCharts(object): """ @@ -77,12 +105,16 @@ class PythonCharts(object): modules=None, modules_path='../python.d/', modules_configs='../conf.d/', - modules_disabled=None): + modules_disabled=None, + modules_enabled=None, + default_run=None): """ :param modules: list :param modules_path: str :param modules_configs: str :param modules_disabled: list + :param modules_enabled: list + :param default_run: bool """ if modules is None: @@ -95,13 +127,13 @@ class PythonCharts(object): self.configs = modules_configs # load modules - loaded_modules = self._load_modules(modules_path, modules, modules_disabled) + loaded_modules = self._load_modules(modules_path, modules, modules_disabled, modules_enabled, default_run) # load configuration files configured_modules = self._load_configs(loaded_modules) # good economy and prosperity: - self.jobs = self._create_jobs(configured_modules) # type: list + self.jobs = self._create_jobs(configured_modules) # type # enable timetable override like `python.d.plugin mysql debug 1` if DEBUG_FLAG and OVERRIDE_UPDATE_EVERY: @@ -131,7 +163,7 @@ class PythonCharts(object): msg.error("Problem loading", name, str(e)) return None - def _load_modules(self, path, modules, disabled): + def _load_modules(self, path, modules, disabled, enabled, default_run): """ Load modules from 'modules' list or dynamically every file from 'path' (only .chart.py files) :param path: str @@ -157,7 +189,10 @@ class PythonCharts(object): msg.fatal('no modules found.') else: # scan directory specified in path and load all modules from there - names = os.listdir(path) + if default_run is False: + names = [module for module in os.listdir(path) if module[:-9] in enabled] + else: + names = os.listdir(path) for mod in names: if mod.replace(MODULE_EXTENSION, "") in disabled: msg.error(mod + ": disabled module ", mod.replace(MODULE_EXTENSION, "")) @@ -235,7 +270,7 @@ class PythonCharts(object): # check if there are dict in config dict many_jobs = False for name in config: - if type(config[name]) is dict: + if isinstance(config[name], DICT): many_jobs = True break @@ -343,7 +378,8 @@ class PythonCharts(object): if job.override_name is not None: new_name = job.__module__ + '_' + sub(r'\s+', '_', job.override_name) if new_name in overridden: - msg.info("DROPPED:", job.name, ", job '" + job.override_name + "' is already served by another job.") + msg.info("DROPPED:", job.name, ", job '" + job.override_name + + "' is already served by another job.") self._stop(job) i -= 1 else: @@ -420,7 +456,10 @@ def read_config(path): """ try: with open(path, 'r') as stream: - config = yaml.load(stream) + if ORDERED: + config = ordered_load(stream, yaml.SafeLoader) + else: + config = yaml.load(stream) except (OSError, IOError): msg.error(str(path), "is not a valid configuration file") return None @@ -452,7 +491,7 @@ def parse_cmdline(directory, *commands): elif cmd == "trace" or cmd == "all": TRACE_FLAG = True elif os.path.isfile(directory + cmd + ".chart.py") or os.path.isfile(directory + cmd): - #DEBUG_FLAG = True + # DEBUG_FLAG = True mods.append(cmd.replace(".chart.py", "")) else: try: @@ -477,7 +516,9 @@ def run(): global DEBUG_FLAG, TRACE_FLAG, BASE_CONFIG # read configuration file - disabled = [] + disabled = ['nginx_log', 'gunicorn_log'] + enabled = list() + default_run = True configfile = CONFIG_DIR + "python.d.conf" msg.PROGRAM = PROGRAM msg.info("reading configuration file:", configfile) @@ -519,12 +560,17 @@ def run(): except (KeyError, TypeError): pass + default_run = True if ('default_run' not in conf or conf.get('default_run')) else False + for k, v in conf.items(): - if k in ("update_every", "debug", "enabled"): + if k in ("update_every", "debug", "enabled", "default_run"): continue - if v is False: - disabled.append(k) - + if default_run: + if v is False: + disabled.append(k) + else: + if v is True: + enabled.append(k) # parse passed command line arguments modules = parse_cmdline(MODULES_DIR, *sys.argv) msg.DEBUG_FLAG = DEBUG_FLAG @@ -539,7 +585,7 @@ def run(): ", ONLY_MODULES=" + str(modules)) # run plugins - charts = PythonCharts(modules, MODULES_DIR, CONFIG_DIR + "python.d/", disabled) + charts = PythonCharts(modules, MODULES_DIR, CONFIG_DIR + "python.d/", disabled, enabled, default_run) charts.check() charts.create() charts.update() diff --git a/plugins.d/tc-qos-helper.sh b/plugins.d/tc-qos-helper.sh index e9253c8f2..074fece9a 100755 --- a/plugins.d/tc-qos-helper.sh +++ b/plugins.d/tc-qos-helper.sh @@ -12,12 +12,58 @@ export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin" export LC_ALL=C + +# ----------------------------------------------------------------------------- +# find /var/run/fireqos + +# the default +fireqos_run_dir="/var/run/fireqos" + +function realdir { + local r="$1" + local t=$(readlink "$r") + + while [ "$t" ] + do + r=$(cd $(dirname "$r") && cd $(dirname "$t") && pwd -P)/$(basename "$t") + t=$(readlink "$r") + done + + dirname "$r" +} + +if [ ! -d "${fireqos_run_dir}" ] + then + + # the fireqos executable - we will use it to find its config + fireqos="$(which fireqos 2>/dev/null || command -v fireqos 2>/dev/null)" + + if [ ! -z "${fireqos}" ] + then + + fireqos_exec_dir="$(realdir ${fireqos})" + + if [ ! -z "${fireqos_exec_dir}" -a "${fireqos_exec_dir}" != "." -a -f "${fireqos_exec_dir}/install.config" ] + then + + LOCALSTATEDIR= + source "${fireqos_exec_dir}/install.config" + + if [ -d "${LOCALSTATEDIR}/run/fireqos" ] + then + fireqos_run_dir="${LOCALSTATEDIR}/run/fireqos" + fi + fi + fi +fi + +# ----------------------------------------------------------------------------- +# logging functions + PROGRAM_FILE="$0" PROGRAM_NAME="$(basename $0)" PROGRAM_NAME="${PROGRAM_NAME/.plugin}" -# ----------------------------------------------------------------------------- - logdate() { date "+%Y-%m-%d %H:%M:%S" } @@ -52,6 +98,7 @@ debug() { [ $debug -eq 1 ] && log DEBUG "${@}" } + # ----------------------------------------------------------------------------- plugins_dir="${NETDATA_PLUGINS_DIR}" @@ -59,23 +106,52 @@ plugins_dir="${NETDATA_PLUGINS_DIR}" config_dir=${NETDATA_CONFIG_DIR-/etc/netdata} tc="$(which tc 2>/dev/null || command -v tc 2>/dev/null)" -fireqos_run_dir="/var/run/fireqos" + + +# ----------------------------------------------------------------------------- +# user configuration + +# time in seconds to refresh QoS class/qdisc names qos_get_class_names_every=120 + +# time in seconds to exit - netdata will restart the script qos_exit_every=3600 +# what to use? classes or qdiscs? +tc_show="qdisc" # can also be "class" + + +# ----------------------------------------------------------------------------- # check if we have a valid number for interval + t=${1} update_every=$((t)) [ $((update_every)) -lt 1 ] && update_every=${NETDATA_UPDATE_EVERY} [ $((update_every)) -lt 1 ] && update_every=1 + +# ----------------------------------------------------------------------------- # allow the user to override our defaults + if [ -f "${config_dir}/tc-qos-helper.conf" ] then source "${config_dir}/tc-qos-helper.conf" fi +case "${tc_show}" in + qdisc|class) + ;; + + *) + error "tc_show variable can be either 'qdisc' or 'class' but is set to '${tc_show}'. Assuming it is 'qdisc'." + tc_show="qdisc" + ;; +esac + + +# ----------------------------------------------------------------------------- # default sleep function + LOOPSLEEPMS_LASTWORK=0 loopsleepms() { sleep $1 @@ -85,6 +161,10 @@ loopsleepms() { # with a high resolution timer function for precise looping. . "${plugins_dir}/loopsleepms.sh.inc" + +# ----------------------------------------------------------------------------- +# final checks we can run + if [ -z "${tc}" -o ! -x "${tc}" ] then fatal "cannot find command 'tc' in this system." @@ -93,11 +173,20 @@ fi tc_devices= fix_names= +# ----------------------------------------------------------------------------- + setclassname() { - echo "SETCLASSNAME $3 $2" + if [ "${tc_show}" = "qdisc" ] + then + echo "SETCLASSNAME $4 $2" + else + echo "SETCLASSNAME $3 $2" + fi } show_tc_cls() { + [ "${tc_show}" = "qdisc" ] && return 1 + local x="${1}" if [ -f /etc/iproute2/tc_cls ] @@ -110,7 +199,6 @@ show_tc_cls() { done &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@ @@ -89,10 +35,14 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ +DIST_COMMON = $(dist_python_DATA) $(dist_python_SCRIPTS) \ + $(dist_pythonmodules_DATA) $(dist_pythonyaml2_DATA) \ + $(dist_pythonyaml3_DATA) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(top_srcdir)/build/subst.inc subdir = python.d 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 \ @@ -101,10 +51,6 @@ 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_python_SCRIPTS) \ - $(dist_python_DATA) $(dist_pythonmodules_DATA) \ - $(dist_pythonyaml2_DATA) $(dist_pythonyaml3_DATA) \ - $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -140,33 +86,13 @@ am__installdirs = "$(DESTDIR)$(pythondir)" "$(DESTDIR)$(pythondir)" \ "$(DESTDIR)$(pythonmodulesdir)" "$(DESTDIR)$(pythonyaml2dir)" \ "$(DESTDIR)$(pythonyaml3dir)" SCRIPTS = $(dist_python_SCRIPTS) -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 = SOURCES = DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac DATA = $(dist_python_DATA) $(dist_pythonmodules_DATA) \ $(dist_pythonyaml2_DATA) $(dist_pythonyaml3_DATA) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/build/subst.inc DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -190,7 +116,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@ @@ -204,6 +134,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@ @@ -307,6 +241,11 @@ CLEANFILES = \ SUFFIXES = .in dist_python_SCRIPTS = \ + python-modules-installer.sh \ + $(NULL) + +dist_python_DATA = \ + README.md \ apache.chart.py \ apache_cache.chart.py \ bind_rndc.chart.py \ @@ -318,16 +257,16 @@ dist_python_SCRIPTS = \ exim.chart.py \ fail2ban.chart.py \ freeradius.chart.py \ - gunicorn_log.chart.py \ haproxy.chart.py \ hddtemp.chart.py \ ipfs.chart.py \ isc_dhcpd.chart.py \ mdstat.chart.py \ memcached.chart.py \ + mongodb.chart.py \ mysql.chart.py \ nginx.chart.py \ - nginx_log.chart.py \ + nsd.chart.py \ ovpn_status_log.chart.py \ phpfpm.chart.py \ postfix.chart.py \ @@ -336,13 +275,10 @@ dist_python_SCRIPTS = \ retroshare.chart.py \ sensors.chart.py \ squid.chart.py \ + smartd_log.chart.py \ tomcat.chart.py \ varnish.chart.py \ - python-modules-installer.sh \ - $(NULL) - -dist_python_DATA = \ - README.md \ + web_log.chart.py \ $(NULL) pythonmodulesdir = $(pythondir)/python_modules @@ -411,6 +347,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu python.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu python.d/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -419,7 +356,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; -$(top_srcdir)/build/subst.inc $(am__empty): +$(top_srcdir)/build/subst.inc: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh @@ -431,11 +368,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_pythonSCRIPTS: $(dist_python_SCRIPTS) @$(NORMAL_INSTALL) + test -z "$(pythondir)" || $(MKDIR_P) "$(DESTDIR)$(pythondir)" @list='$(dist_python_SCRIPTS)'; test -n "$(pythondir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pythondir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pythondir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ @@ -466,11 +400,8 @@ uninstall-dist_pythonSCRIPTS: dir='$(DESTDIR)$(pythondir)'; $(am__uninstall_files_from_dir) install-dist_pythonDATA: $(dist_python_DATA) @$(NORMAL_INSTALL) + test -z "$(pythondir)" || $(MKDIR_P) "$(DESTDIR)$(pythondir)" @list='$(dist_python_DATA)'; test -n "$(pythondir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pythondir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pythondir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -487,11 +418,8 @@ uninstall-dist_pythonDATA: dir='$(DESTDIR)$(pythondir)'; $(am__uninstall_files_from_dir) install-dist_pythonmodulesDATA: $(dist_pythonmodules_DATA) @$(NORMAL_INSTALL) + test -z "$(pythonmodulesdir)" || $(MKDIR_P) "$(DESTDIR)$(pythonmodulesdir)" @list='$(dist_pythonmodules_DATA)'; test -n "$(pythonmodulesdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pythonmodulesdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pythonmodulesdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -508,11 +436,8 @@ uninstall-dist_pythonmodulesDATA: dir='$(DESTDIR)$(pythonmodulesdir)'; $(am__uninstall_files_from_dir) install-dist_pythonyaml2DATA: $(dist_pythonyaml2_DATA) @$(NORMAL_INSTALL) + test -z "$(pythonyaml2dir)" || $(MKDIR_P) "$(DESTDIR)$(pythonyaml2dir)" @list='$(dist_pythonyaml2_DATA)'; test -n "$(pythonyaml2dir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pythonyaml2dir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pythonyaml2dir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -529,11 +454,8 @@ uninstall-dist_pythonyaml2DATA: dir='$(DESTDIR)$(pythonyaml2dir)'; $(am__uninstall_files_from_dir) install-dist_pythonyaml3DATA: $(dist_pythonyaml3_DATA) @$(NORMAL_INSTALL) + test -z "$(pythonyaml3dir)" || $(MKDIR_P) "$(DESTDIR)$(pythonyaml3dir)" @list='$(dist_pythonyaml3_DATA)'; test -n "$(pythonyaml3dir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pythonyaml3dir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pythonyaml3dir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -548,11 +470,11 @@ uninstall-dist_pythonyaml3DATA: @list='$(dist_pythonyaml3_DATA)'; test -n "$(pythonyaml3dir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pythonyaml3dir)'; $(am__uninstall_files_from_dir) -tags TAGS: - -ctags CTAGS: +tags: TAGS +TAGS: -cscope cscopelist: +ctags: CTAGS +CTAGS: distdir: $(DISTFILES) @@ -696,24 +618,22 @@ uninstall-am: uninstall-dist_pythonDATA uninstall-dist_pythonSCRIPTS \ .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ - ctags-am distclean distclean-generic distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dist_pythonDATA \ - install-dist_pythonSCRIPTS install-dist_pythonmodulesDATA \ - install-dist_pythonyaml2DATA install-dist_pythonyaml3DATA \ - 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-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags-am \ - uninstall uninstall-am uninstall-dist_pythonDATA \ - uninstall-dist_pythonSCRIPTS uninstall-dist_pythonmodulesDATA \ +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-dist_pythonDATA install-dist_pythonSCRIPTS \ + install-dist_pythonmodulesDATA install-dist_pythonyaml2DATA \ + install-dist_pythonyaml3DATA 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-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am uninstall uninstall-am \ + uninstall-dist_pythonDATA uninstall-dist_pythonSCRIPTS \ + uninstall-dist_pythonmodulesDATA \ uninstall-dist_pythonyaml2DATA uninstall-dist_pythonyaml3DATA -.PRECIOUS: Makefile - .in: if sed \ -e 's#[@]localstatedir_POST@#$(localstatedir)#g' \ diff --git a/python.d/README.md b/python.d/README.md index 75f5614a7..7df6e3e86 100644 --- a/python.d/README.md +++ b/python.d/README.md @@ -704,6 +704,149 @@ If no configuration is given, module will attempt to connect to memcached instan --- +# mongodb + +Module monitor mongodb performance and health metrics + +**Requirements:** + * `python-pymongo` package. + +You need to install it manually. + + +Number of charts depends on mongodb version, storage engine and other features (replication): + +1. **Read requests**: + * query + * getmore (operation the cursor executes to get additional data from query) + +2. **Write requests**: + * insert + * delete + * update + +3. **Active clients**: + * readers (number of clients with read operations in progress or queued) + * writers (number of clients with write operations in progress or queued) + +4. **Journal transactions**: + * commits (count of transactions that have been written to the journal) + +5. **Data written to the journal**: + * volume (volume of data) + +6. **Background flush** (MMAPv1): + * average ms (average time taken by flushes to execute) + * last ms (time taken by the last flush) + +8. **Read tickets** (WiredTiger): + * in use (number of read tickets in use) + * available (number of available read tickets remaining) + +9. **Write tickets** (WiredTiger): + * in use (number of write tickets in use) + * available (number of available write tickets remaining) + +10. **Cursors**: + * opened (number of cursors currently opened by MongoDB for clients) + * timedOut (number of cursors that have timed) + * noTimeout (number of open cursors with timeout disabled) + +11. **Connections**: + * connected (number of clients currently connected to the database server) + * unused (number of unused connections available for new clients) + +12. **Memory usage metrics**: + * virtual + * resident (amount of memory used by the database process) + * mapped + * non mapped + +13. **Page faults**: + * page faults (number of times MongoDB had to request from disk) + +14. **Cache metrics** (WiredTiger): + * percentage of bytes currently in the cache (amount of space taken by cached data) + * percantage of tracked dirty bytes in the cache (amount of space taken by dirty data) + +15. **Pages evicted from cache** (WiredTiger): + * modified + * unmodified + +16. **Queued requests**: + * readers (number of read request currently queued) + * writers (number of write request currently queued) + +17. **Errors**: + * msg (number of message assertions raised) + * warning (number of warning assertions raised) + * regular (number of regular assertions raised) + * user (number of assertions corresponding to errors generated by users) + +18. **Storage metrics** (one chart for every database) + * dataSize (size of all documents + padding in the database) + * indexSize (size of all indexes in the database) + * storageSize (size of all extents in the database) + +19. **Documents in the database** (one chart for all databases) + * documents (number of objects in the database among all the collections) + +20. **tcmalloc metrics** + * central cache free + * current total thread cache + * pageheap free + * pageheap unmapped + * thread cache free + * transfer cache free + * heap size + +21. **Commands total/failed rate** + * count + * createIndex + * delete + * eval + * findAndModify + * insert + +22. **Locks metrics** (acquireCount metrics - number of times the lock was acquired in the specified mode) + * Global lock + * Database lock + * Collection lock + * Metadata lock + * oplog lock + +23. **Replica set members state** + * state + +24. **Oplog window** + * window (interval of time between the oldest and the latest entries in the oplog) + +25. **Replication lag** + * member (time when last entry from the oplog was applied for every member) + +26. **Replication set member heartbeat latency** + * member (time when last heartbeat was received from replica set member) + + +### configuration + +Sample: + +```yaml +local: + name : 'local' + host : '127.0.0.1' + port : 27017 + user : 'netdata' + pass : 'netdata' + +``` + +If no configuration is given, module will attempt to connect to mongodb daemon on `127.0.0.1:27017` address + +--- + + # mysql Module monitors one or more mysql servers @@ -841,30 +984,58 @@ Without configuration, module attempts to connect to `http://localhost/stub_stat --- -# nginx_log +# nsd -Module monitors nginx access log and produces only one chart: +Module uses the `nsd-control stats_noreset` command to provide `nsd` statistics. -1. **nginx status codes** in requests/s - * 2xx - * 3xx - * 4xx - * 5xx +**Requirements:** + * Version of `nsd` must be 4.0+ + * Netdata must have permissions to run `nsd-control stats_noreset` -### configuration +It produces: -Sample for two vhosts: +1. **Queries** + * queries -```yaml -site_A: - path: '/var/log/nginx/access-A.log' +2. **Zones** + * master + * slave -site_B: - name: 'local' - path: '/var/log/nginx/access-B.log' -``` +3. **Protocol** + * udp + * udp6 + * tcp + * tcp6 + +4. **Query Type** + * A + * NS + * CNAME + * SOA + * PTR + * HINFO + * MX + * NAPTR + * TXT + * AAAA + * SRV + * ANY + +5. **Transfer** + * NOTIFY + * AXFR + +6. **Return Code** + * NOERROR + * FORMERR + * SERVFAIL + * NXDOMAIN + * NOTIMP + * REFUSED + * YXDOMAIN -When no configuration file is found, module tries to parse `/var/log/nginx/access.log` file. + +Configuration is not needed. --- @@ -958,6 +1129,75 @@ Configuration is not needed. --- +# postgres + +Module monitors one or more postgres servers. + +**Requirements:** + + * `python-psycopg2` package. You have to install to manually. + +Following charts are drawn: + +1. **Database size** MB + * size + +2. **Current Backend Processes** processes + * active + +3. **Write-Ahead Logging Statistics** files/s + * total + * ready + * done + +4. **Checkpoints** writes/s + * scheduled + * requested + +5. **Current connections to db** count + * connections + +6. **Tuples returned from db** tuples/s + * sequential + * bitmap + +7. **Tuple reads from db** reads/s + * disk + * cache + +8. **Transactions on db** transactions/s + * commited + * rolled back + +9. **Tuples written to db** writes/s + * inserted + * updated + * deleted + * conflicts + +10. **Locks on db** count per type + * locks + +### configuration + +```yaml +socket: + name : 'socket' + user : 'postgres' + database : 'postgres' + +tcp: + name : 'tcp' + user : 'postgres' + database : 'postgres' + host : 'localhost' + port : 5432 +``` + +When no configuration file is found, module tries to connect to TCP/IP socket: `localhost:5432`. + +--- + # redis Get INFO data from redis instance. @@ -1052,6 +1292,45 @@ Without any configuration module will try to autodetect where squid presents its --- +# smartd_log + +Module monitor `smartd` log files to collect HDD/SSD S.M.A.R.T attributes. + +It produces following charts (you can add additional attributes in the module configuration file): + +1. **Read Error Rate** attribute 1 + +2. **Start/Stop Count** attribute 4 + +3. **Reallocated Sectors Count** attribute 5 + +4. **Seek Error Rate** attribute 7 + +5. **Power-On Hours Count** attribute 9 + +6. **Power Cycle Count** attribute 12 + +7. **Load/Unload Cycles** attribute 193 + +8. **Temperature** attribute 194 + +9. **Current Pending Sectors** attribute 197 + +10. **Off-Line Uncorrectable** attribute 198 + +11. **Write Error Rate** attribute 200 + +### configuration + +```yaml +local: + log_path : '/var/log/smartd/' +``` + +If no configuration is given, module will attempt to read log files in /var/log/smartd/ directory. + +--- + # tomcat Present tomcat containers memory utilization. @@ -1151,3 +1430,68 @@ It produces: No configuration is needed. --- + +# web_log + +Tails the apache/nginx/lighttpd/gunicorn log files to collect real-time web-server statistics. + +It produces following charts: + +1. **Response by type** requests/s + * success (1xx, 2xx, 304) + * error (5xx) + * redirect (3xx except 304) + * bad (4xx) + * other (all other responses) + +2. **Response by code family** requests/s + * 1xx (informational) + * 2xx (successful) + * 3xx (redirect) + * 4xx (bad) + * 5xx (internal server errors) + * other (non-standart responses) + * unmatched (the lines in the log file that are not matched) + +3. **Detailed Response Codes** requests/s (number of responses for each response code family individually) + +4. **Bandwidth** KB/s + * received (bandwidth of requests) + * send (bandwidth of responses) + +5. **Timings** ms (request processing time) + * min (bandwidth of requests) + * max (bandwidth of responses) + * average (bandwidth of responses) + +6. **Request per url** requests/s (configured by user) + +7. **Http Methods** requests/s (requests per http method) + +8. **Http Versions** requests/s (requests per http version) + +9. **IP protocols** requests/s (requests per ip protocol version) + +10. **Curent Poll Unique Client IPs** unique ips/s (unique client IPs per data collection iteration) + +11. **All Time Unique Client IPs** unique ips/s (unique client IPs since the last restart of netdata) + + +### configuration + +```yaml +nginx_log: + name : 'nginx_log' + path : '/var/log/nginx/access.log' + +apache_log: + name : 'apache_log' + path : '/var/log/apache/other_vhosts_access.log' + categories: + cacti : 'cacti.*' + observium : 'observium' +``` + +Module has preconfigured jobs for nginx, apache and gunicorn on various distros. + +--- diff --git a/python.d/bind_rndc.chart.py b/python.d/bind_rndc.chart.py index e11c751f5..a4d753703 100644 --- a/python.d/bind_rndc.chart.py +++ b/python.d/bind_rndc.chart.py @@ -4,7 +4,7 @@ from base import SimpleService from re import compile, findall -from os.path import getsize, isfile, split +from os.path import getsize, split from os import access as is_accessible, R_OK from subprocess import Popen @@ -12,7 +12,6 @@ priority = 60000 retries = 60 update_every = 30 -DIRECTORIES = ['/bin/', '/usr/bin/', '/sbin/', '/usr/sbin/'] NMS = ['requests', 'responses', 'success', 'auth_answer', 'nonauth_answer', 'nxrrset', 'failure', 'nxdomain', 'recursion', 'duplicate', 'rejections'] QUERIES = ['RESERVED0', 'A', 'NS', 'CNAME', 'SOA', 'PTR', 'MX', 'TXT', 'X25', 'AAAA', 'SRV', 'NAPTR', @@ -29,16 +28,12 @@ class Service(SimpleService): # 'Cache DB RRsets', 'Socket I/O Statistics'] self.options = ['Name Server Statistics', 'Incoming Queries', 'Outgoing Queries'] self.regex_options = [r'(%s(?= \+\+)) \+\+([^\+]+)' % option for option in self.options] - try: - self.rndc = [''.join([directory, 'rndc']) for directory in DIRECTORIES - if isfile(''.join([directory, 'rndc']))][0] - except IndexError: - self.rndc = False + self.rndc = self.find_binary('rndc') def check(self): # We cant start without 'rndc' command if not self.rndc: - self.error('Command "rndc" not found') + self.error('Can\'t locate \'rndc\' binary or binary is not executable by netdata') return False # We cant start if stats file is not exist or not readable by netdata user @@ -55,15 +50,15 @@ class Service(SimpleService): if not run_rndc.returncode: # 'rndc' was found, stats file is exist and readable and we can run 'rndc stats'. Lets go! self.create_charts() - + # BIND APPEND dump on every run 'rndc stats' # that is why stats file size can be VERY large if update_interval too small dump_size_24hr = round(86400 / self.update_every * (int(size_after) - int(size_before)) / 1048576, 3) - + # If update_every too small we should WARN user if self.update_every < 30: self.info('Update_every %s is NOT recommended for use. Increase the value to > 30' % self.update_every) - + self.info('With current update_interval it will be + %s MB every 24hr. ' 'Don\'t forget to create logrotate conf file for %s' % (dump_size_24hr, self.named_stats_path)) @@ -82,12 +77,11 @@ class Service(SimpleService): named.stats file size ) """ - try: current_size = getsize(self.named_stats_path) except OSError: return None, None - + run_rndc = Popen([self.rndc, 'stats'], shell=False) run_rndc.wait() @@ -115,23 +109,23 @@ class Service(SimpleService): return None rndc_stats = dict() - + # Result: dict. # topic = Cache DB RRsets; body = A 178303 NS 86790 ... ; desc = A; value = 178303 # {'Cache DB RRsets': [('A', 178303), ('NS', 286790), ...], # {Incoming Queries': [('RESERVED0', 8), ('A', 4557317680), ...], # ...... for regex in self.regex_options: - rndc_stats.update({topic: [(desc, int(value)) for value, desc in self.regex_values.findall(body)] - for topic, body in findall(regex, raw_data)}) - + rndc_stats.update(dict([(topic, [(desc, int(value)) for value, desc in self.regex_values.findall(body)]) + for topic, body in findall(regex, raw_data)])) + nms = dict(rndc_stats.get('Name Server Statistics', [])) - inc_queries = {'i' + k: 0 for k in QUERIES} - inc_queries.update({'i' + k: v for k, v in rndc_stats.get('Incoming Queries', [])}) - out_queries = {'o' + k: 0 for k in QUERIES} - out_queries.update({'o' + k: v for k, v in rndc_stats.get('Outgoing Queries', [])}) - + inc_queries = dict([('i' + k, 0) for k in QUERIES]) + inc_queries.update(dict([('i' + k, v) for k, v in rndc_stats.get('Incoming Queries', [])])) + out_queries = dict([('o' + k, 0) for k in QUERIES]) + out_queries.update(dict([('o' + k, v) for k, v in rndc_stats.get('Outgoing Queries', [])])) + to_netdata = dict() to_netdata['requests'] = sum([v for k, v in nms.items() if 'request' in k and 'received' in k]) to_netdata['responses'] = sum([v for k, v in nms.items() if 'responses' in k and 'sent' in k]) @@ -145,7 +139,7 @@ class Service(SimpleService): to_netdata['duplicate'] = nms.get('duplicate queries received', 0) to_netdata['rejections'] = nms.get('recursive queries rejected', 0) to_netdata['stats_size'] = size - + to_netdata.update(inc_queries) to_netdata.update(out_queries) return to_netdata @@ -154,20 +148,20 @@ class Service(SimpleService): self.order = ['stats_size', 'bind_stats', 'incoming_q', 'outgoing_q'] self.definitions = { 'bind_stats': { - 'options': [None, 'Name Server Statistics', 'stats', 'Name Server Statistics', 'bind_rndc.stats', 'line'], + 'options': [None, 'Name Server Statistics', 'stats', 'name server statistics', 'bind_rndc.stats', 'line'], 'lines': [ ]}, 'incoming_q': { - 'options': [None, 'Incoming queries', 'queries','Incoming queries', 'bind_rndc.incq', 'line'], + 'options': [None, 'Incoming queries', 'queries','incoming queries', 'bind_rndc.incq', 'line'], 'lines': [ ]}, 'outgoing_q': { - 'options': [None, 'Outgoing queries', 'queries','Outgoing queries', 'bind_rndc.outq', 'line'], + 'options': [None, 'Outgoing queries', 'queries','outgoing queries', 'bind_rndc.outq', 'line'], 'lines': [ ]}, 'stats_size': { 'options': [None, '%s file size' % split(self.named_stats_path)[1].capitalize(), 'megabytes', - '%s size' % split(self.named_stats_path)[1].capitalize(), 'bind_rndc.size', 'line'], + '%s size' % split(self.named_stats_path)[1], 'bind_rndc.size', 'line'], 'lines': [ ["stats_size", None, "absolute", 1, 1048576] ]} diff --git a/python.d/cpufreq.chart.py b/python.d/cpufreq.chart.py index c761aae88..e28bdea8f 100644 --- a/python.d/cpufreq.chart.py +++ b/python.d/cpufreq.chart.py @@ -62,6 +62,7 @@ class Service(SimpleService): return data else: self.alert("accurate method failed, falling back") + self.accurate_exists = False for name, paths in self.assignment.items(): diff --git a/python.d/elasticsearch.chart.py b/python.d/elasticsearch.chart.py index ff841f17c..430227f69 100644 --- a/python.d/elasticsearch.chart.py +++ b/python.d/elasticsearch.chart.py @@ -3,13 +3,14 @@ # Author: l2isbad from base import UrlService -from requests import get -from socket import gethostbyname +from socket import gethostbyname, gaierror try: from queue import Queue except ImportError: from Queue import Queue from threading import Thread +from collections import namedtuple +from json import loads # default module values (can be overridden per job in `config`) # update_every = 2 @@ -17,45 +18,125 @@ update_every = 5 priority = 60000 retries = 60 +METHODS = namedtuple('METHODS', ['get_data_function', 'url']) + +NODE_STATS = [ + ('indices.search.fetch_current', None, None), + ('indices.search.fetch_total', None, None), + ('indices.search.query_current', None, None), + ('indices.search.query_total', None, None), + ('indices.search.query_time_in_millis', None, None), + ('indices.search.fetch_time_in_millis', None, None), + ('indices.indexing.index_total', 'indexing_index_total', None), + ('indices.indexing.index_current', 'indexing_index_current', None), + ('indices.indexing.index_time_in_millis', 'indexing_index_time_in_millis', None), + ('indices.refresh.total', 'refresh_total', None), + ('indices.refresh.total_time_in_millis', 'refresh_total_time_in_millis', None), + ('indices.flush.total', 'flush_total', None), + ('indices.flush.total_time_in_millis', 'flush_total_time_in_millis', None), + ('jvm.gc.collectors.young.collection_count', 'young_collection_count', None), + ('jvm.gc.collectors.old.collection_count', 'old_collection_count', None), + ('jvm.gc.collectors.young.collection_time_in_millis', 'young_collection_time_in_millis', None), + ('jvm.gc.collectors.old.collection_time_in_millis', 'old_collection_time_in_millis', None), + ('jvm.mem.heap_used_percent', 'jvm_heap_percent', None), + ('jvm.mem.heap_committed_in_bytes', 'jvm_heap_commit', None), + ('thread_pool.bulk.queue', 'bulk_queue', None), + ('thread_pool.bulk.rejected', 'bulk_rejected', None), + ('thread_pool.index.queue', 'index_queue', None), + ('thread_pool.index.rejected', 'index_rejected', None), + ('thread_pool.search.queue', 'search_queue', None), + ('thread_pool.search.rejected', 'search_rejected', None), + ('thread_pool.merge.queue', 'merge_queue', None), + ('thread_pool.merge.rejected', 'merge_rejected', None), + ('indices.fielddata.memory_size_in_bytes', 'index_fdata_memory', None), + ('indices.fielddata.evictions', None, None), + ('breakers.fielddata.tripped', None, None), + ('http.current_open', 'http_current_open', None), + ('transport.rx_size_in_bytes', 'transport_rx_size_in_bytes', None), + ('transport.tx_size_in_bytes', 'transport_tx_size_in_bytes', None), + ('process.max_file_descriptors', None, None), + ('process.open_file_descriptors', None, None) +] + +CLUSTER_STATS = [ + ('nodes.count.data_only', 'count_data_only', None), + ('nodes.count.master_data', 'count_master_data', None), + ('nodes.count.total', 'count_total', None), + ('nodes.count.master_only', 'count_master_only', None), + ('nodes.count.client', 'count_client', None), + ('indices.docs.count', 'docs_count', None), + ('indices.query_cache.hit_count', 'query_cache_hit_count', None), + ('indices.query_cache.miss_count', 'query_cache_miss_count', None), + ('indices.store.size_in_bytes', 'store_size_in_bytes', None), + ('indices.count', 'indices_count', None), + ('indices.shards.total', 'shards_total', None) +] + +HEALTH_STATS = [ + ('number_of_nodes', 'health_number_of_nodes', None), + ('number_of_data_nodes', 'health_number_of_data_nodes', None), + ('number_of_pending_tasks', 'health_number_of_pending_tasks', None), + ('number_of_in_flight_fetch', 'health_number_of_in_flight_fetch', None), + ('active_shards', 'health_active_shards', None), + ('relocating_shards', 'health_relocating_shards', None), + ('unassigned_shards', 'health_unassigned_shards', None), + ('delayed_unassigned_shards', 'health_delayed_unassigned_shards', None), + ('initializing_shards', 'health_initializing_shards', None), + ('active_shards_percent_as_number', 'health_active_shards_percent_as_number', None) +] + # charts order (can be overridden if you want less charts, or different order) -ORDER = ['search_perf_total', 'search_perf_time', 'search_latency', 'index_perf_total', 'index_perf_time', - 'index_latency', 'jvm_mem_heap', 'jvm_gc_count', 'jvm_gc_time', 'host_metrics_file_descriptors', - 'host_metrics_http', 'host_metrics_transport', 'thread_pool_qr', 'fdata_cache', 'fdata_ev_tr', - 'cluster_health_status', 'cluster_health_nodes', 'cluster_health_shards', 'cluster_stats_nodes', - 'cluster_stats_query_cache', 'cluster_stats_docs', 'cluster_stats_store', 'cluster_stats_indices_shards'] +ORDER = ['search_perf_total', 'search_perf_current', 'search_perf_time', 'search_latency', 'index_perf_total', + 'index_perf_current', 'index_perf_time', 'index_latency', 'jvm_mem_heap', 'jvm_gc_count', + 'jvm_gc_time', 'host_metrics_file_descriptors', 'host_metrics_http', 'host_metrics_transport', + 'thread_pool_qr_q', 'thread_pool_qr_r', 'fdata_cache', 'fdata_ev_tr', 'cluster_health_status', + 'cluster_health_nodes', 'cluster_health_shards', 'cluster_stats_nodes', 'cluster_stats_query_cache', + 'cluster_stats_docs', 'cluster_stats_store', 'cluster_stats_indices_shards'] CHARTS = { 'search_perf_total': { - 'options': [None, 'Number of queries, fetches', 'queries', 'Search performance', 'es.search_query', 'stacked'], + 'options': [None, 'Total number of queries, fetches', 'number of', 'search performance', + 'es.search_query_total', 'stacked'], + 'lines': [ + ['query_total', 'queries', 'incremental'], + ['fetch_total', 'fetches', 'incremental'] + ]}, + 'search_perf_current': { + 'options': [None, 'Number of queries, fetches in progress', 'number of', 'search performance', + 'es.search_query_current', 'stacked'], 'lines': [ - ['query_total', 'search_total', 'incremental'], - ['fetch_total', 'fetch_total', 'incremental'], - ['query_current', 'search_current', 'absolute'], - ['fetch_current', 'fetch_current', 'absolute'] + ['query_current', 'queries', 'absolute'], + ['fetch_current', 'fetches', 'absolute'] ]}, 'search_perf_time': { - 'options': [None, 'Time spent on queries, fetches', 'seconds', 'Search performance', 'es.search_time', 'stacked'], + 'options': [None, 'Time spent on queries, fetches', 'seconds', 'search performance', + 'es.search_time', 'stacked'], 'lines': [ ['query_time_in_millis', 'query', 'incremental', 1, 1000], ['fetch_time_in_millis', 'fetch', 'incremental', 1, 1000] ]}, 'search_latency': { - 'options': [None, 'Query and fetch latency', 'ms', 'Search performance', 'es.search_latency', 'stacked'], + 'options': [None, 'Query and fetch latency', 'ms', 'search performance', 'es.search_latency', 'stacked'], 'lines': [ ['query_latency', 'query', 'absolute', 1, 1000], ['fetch_latency', 'fetch', 'absolute', 1, 1000] ]}, 'index_perf_total': { - 'options': [None, 'Number of documents indexed, index refreshes, flushes', 'documents/indexes', - 'Indexing performance', 'es.index_doc', 'stacked'], + 'options': [None, 'Total number of documents indexed, index refreshes, index flushes to disk', 'number of', + 'indexing performance', 'es.index_performance_total', 'stacked'], 'lines': [ ['indexing_index_total', 'indexed', 'incremental'], ['refresh_total', 'refreshes', 'incremental'], - ['flush_total', 'flushes', 'incremental'], - ['indexing_index_current', 'indexed_current', 'absolute'], + ['flush_total', 'flushes', 'incremental'] + ]}, + 'index_perf_current': { + 'options': [None, 'Number of documents currently being indexed', 'currently indexed', + 'indexing performance', 'es.index_performance_current', 'stacked'], + 'lines': [ + ['indexing_index_current', 'documents', 'absolute'] ]}, 'index_perf_time': { - 'options': [None, 'Time spent on indexing, refreshing, flushing', 'seconds', 'Indexing performance', + 'options': [None, 'Time spent on indexing, refreshing, flushing', 'seconds', 'indexing performance', 'es.search_time', 'stacked'], 'lines': [ ['indexing_index_time_in_millis', 'indexing', 'incremental', 1, 1000], @@ -63,67 +144,72 @@ CHARTS = { ['flush_total_time_in_millis', 'flushing', 'incremental', 1, 1000] ]}, 'index_latency': { - 'options': [None, 'Indexing and flushing latency', 'ms', 'Indexing performance', + 'options': [None, 'Indexing and flushing latency', 'ms', 'indexing performance', 'es.index_latency', 'stacked'], 'lines': [ ['indexing_latency', 'indexing', 'absolute', 1, 1000], ['flushing_latency', 'flushing', 'absolute', 1, 1000] ]}, 'jvm_mem_heap': { - 'options': [None, 'JVM heap currently in use/committed', 'percent/MB', 'Memory usage and gc', + 'options': [None, 'JVM heap currently in use/committed', 'percent/MB', 'memory usage and gc', 'es.jvm_heap', 'area'], 'lines': [ ['jvm_heap_percent', 'inuse', 'absolute'], ['jvm_heap_commit', 'commit', 'absolute', -1, 1048576] ]}, 'jvm_gc_count': { - 'options': [None, 'Count of garbage collections', 'counts', 'Memory usage and gc', 'es.gc_count', 'stacked'], + 'options': [None, 'Count of garbage collections', 'counts', 'memory usage and gc', 'es.gc_count', 'stacked'], 'lines': [ ['young_collection_count', 'young', 'incremental'], ['old_collection_count', 'old', 'incremental'] ]}, 'jvm_gc_time': { - 'options': [None, 'Time spent on garbage collections', 'ms', 'Memory usage and gc', 'es.gc_time', 'stacked'], + 'options': [None, 'Time spent on garbage collections', 'ms', 'memory usage and gc', 'es.gc_time', 'stacked'], 'lines': [ ['young_collection_time_in_millis', 'young', 'incremental'], ['old_collection_time_in_millis', 'old', 'incremental'] ]}, - 'thread_pool_qr': { - 'options': [None, 'Number of queued/rejected threads in thread pool', 'threads', 'Queues and rejections', - 'es.qr', 'stacked'], + 'thread_pool_qr_q': { + 'options': [None, 'Number of queued threads in thread pool', 'queued threads', 'queues and rejections', + 'es.thread_pool_queued', 'stacked'], 'lines': [ - ['bulk_queue', 'bulk_queue', 'absolute'], - ['index_queue', 'index_queue', 'absolute'], - ['search_queue', 'search_queue', 'absolute'], - ['merge_queue', 'merge_queue', 'absolute'], - ['bulk_rejected', 'bulk_rej', 'absolute'], - ['index_rejected', 'index_rej', 'absolute'], - ['search_rejected', 'search_rej', 'absolute'], - ['merge_rejected', 'merge_rej', 'absolute'] + ['bulk_queue', 'bulk', 'absolute'], + ['index_queue', 'index', 'absolute'], + ['search_queue', 'search', 'absolute'], + ['merge_queue', 'merge', 'absolute'] + ]}, + 'thread_pool_qr_r': { + 'options': [None, 'Number of rejected threads in thread pool', 'rejected threads', 'queues and rejections', + 'es.thread_pool_rejected', 'stacked'], + 'lines': [ + ['bulk_rejected', 'bulk', 'absolute'], + ['index_rejected', 'index', 'absolute'], + ['search_rejected', 'search', 'absolute'], + ['merge_rejected', 'merge', 'absolute'] ]}, 'fdata_cache': { - 'options': [None, 'Fielddata cache size', 'MB', 'Fielddata cache', 'es.fdata_cache', 'line'], + 'options': [None, 'Fielddata cache size', 'MB', 'fielddata cache', 'es.fdata_cache', 'line'], 'lines': [ - ['index_fdata_mem', 'mem_size', 'absolute', 1, 1048576] + ['index_fdata_memory', 'cache', 'absolute', 1, 1048576] ]}, 'fdata_ev_tr': { 'options': [None, 'Fielddata evictions and circuit breaker tripped count', 'number of events', - 'Fielddata cache', 'es.fdata_ev_tr', 'line'], + 'fielddata cache', 'es.evictions_tripped', 'line'], 'lines': [ - ['index_fdata_evic', 'evictions', 'incremental'], - ['breakers_fdata_trip', 'tripped', 'incremental'] + ['evictions', None, 'incremental'], + ['tripped', None, 'incremental'] ]}, 'cluster_health_nodes': { - 'options': [None, 'Nodes and tasks statistics', 'units', 'Cluster health API', - 'es.cluster_health', 'stacked'], + 'options': [None, 'Nodes and tasks statistics', 'units', 'cluster health API', + 'es.cluster_health_nodes', 'stacked'], 'lines': [ ['health_number_of_nodes', 'nodes', 'absolute'], ['health_number_of_data_nodes', 'data_nodes', 'absolute'], ['health_number_of_pending_tasks', 'pending_tasks', 'absolute'], - ['health_number_of_in_flight_fetch', 'inflight_fetch', 'absolute'] + ['health_number_of_in_flight_fetch', 'in_flight_fetch', 'absolute'] ]}, 'cluster_health_status': { - 'options': [None, 'Cluster status', 'status', 'Cluster health API', + 'options': [None, 'Cluster status', 'status', 'cluster health API', 'es.cluster_health_status', 'area'], 'lines': [ ['status_green', 'green', 'absolute'], @@ -134,8 +220,8 @@ CHARTS = { ['status_yellow', 'yellow', 'absolute'] ]}, 'cluster_health_shards': { - 'options': [None, 'Shards statistics', 'shards', 'Cluster health API', - 'es.cluster_health_sharts', 'stacked'], + 'options': [None, 'Shards statistics', 'shards', 'cluster health API', + 'es.cluster_health_shards', 'stacked'], 'lines': [ ['health_active_shards', 'active_shards', 'absolute'], ['health_relocating_shards', 'relocating_shards', 'absolute'], @@ -145,8 +231,8 @@ CHARTS = { ['health_active_shards_percent_as_number', 'active_percent', 'absolute'] ]}, 'cluster_stats_nodes': { - 'options': [None, 'Nodes statistics', 'nodes', 'Cluster stats API', - 'es.cluster_stats_nodes', 'stacked'], + 'options': [None, 'Nodes statistics', 'nodes', 'cluster stats API', + 'es.cluster_nodes', 'stacked'], 'lines': [ ['count_data_only', 'data_only', 'absolute'], ['count_master_data', 'master_data', 'absolute'], @@ -155,47 +241,47 @@ CHARTS = { ['count_client', 'client', 'absolute'] ]}, 'cluster_stats_query_cache': { - 'options': [None, 'Query cache statistics', 'queries', 'Cluster stats API', - 'es.cluster_stats_query_cache', 'stacked'], + 'options': [None, 'Query cache statistics', 'queries', 'cluster stats API', + 'es.cluster_query_cache', 'stacked'], 'lines': [ ['query_cache_hit_count', 'hit', 'incremental'], ['query_cache_miss_count', 'miss', 'incremental'] ]}, 'cluster_stats_docs': { - 'options': [None, 'Docs statistics', 'count', 'Cluster stats API', - 'es.cluster_stats_docs', 'line'], + 'options': [None, 'Docs statistics', 'count', 'cluster stats API', + 'es.cluster_docs', 'line'], 'lines': [ ['docs_count', 'docs', 'absolute'] ]}, 'cluster_stats_store': { - 'options': [None, 'Store statistics', 'MB', 'Cluster stats API', - 'es.cluster_stats_store', 'line'], + 'options': [None, 'Store statistics', 'MB', 'cluster stats API', + 'es.cluster_store', 'line'], 'lines': [ ['store_size_in_bytes', 'size', 'absolute', 1, 1048567] ]}, 'cluster_stats_indices_shards': { - 'options': [None, 'Indices and shards statistics', 'count', 'Cluster stats API', - 'es.cluster_stats_ind_sha', 'stacked'], + 'options': [None, 'Indices and shards statistics', 'count', 'cluster stats API', + 'es.cluster_indices_shards', 'stacked'], 'lines': [ ['indices_count', 'indices', 'absolute'], ['shards_total', 'shards', 'absolute'] ]}, 'host_metrics_transport': { - 'options': [None, 'Cluster communication transport metrics', 'kbit/s', 'Host metrics', - 'es.host_metrics_transport', 'area'], + 'options': [None, 'Cluster communication transport metrics', 'kbit/s', 'host metrics', + 'es.host_transport', 'area'], 'lines': [ ['transport_rx_size_in_bytes', 'in', 'incremental', 8, 1000], ['transport_tx_size_in_bytes', 'out', 'incremental', -8, 1000] ]}, 'host_metrics_file_descriptors': { - 'options': [None, 'Available file descriptors in percent', 'percent', 'Host metrics', - 'es.host_metrics_descriptors', 'area'], + 'options': [None, 'Available file descriptors in percent', 'percent', 'host metrics', + 'es.host_descriptors', 'area'], 'lines': [ ['file_descriptors_used', 'used', 'absolute', 1, 10] ]}, 'host_metrics_http': { - 'options': [None, 'Opened HTTP connections', 'connections', 'Host metrics', - 'es.host_metrics_http', 'line'], + 'options': [None, 'Opened HTTP connections', 'connections', 'host metrics', + 'es.host_http_connections', 'line'], 'lines': [ ['http_current_open', 'opened', 'absolute', 1, 1] ]} @@ -208,78 +294,72 @@ class Service(UrlService): self.order = ORDER self.definitions = CHARTS self.host = self.configuration.get('host') - self.port = self.configuration.get('port') - self.user = self.configuration.get('user') - self.password = self.configuration.get('pass') + self.port = self.configuration.get('port', 9200) + self.scheme = self.configuration.get('scheme', 'http') self.latency = dict() + self.methods = list() def check(self): # We can't start if AND not specified - if not all([self.host, self.port]): + if not all([self.host, self.port, isinstance(self.host, str), isinstance(self.port, (str, int))]): + self.error('Host is not defined in the module configuration file') return False # It as a bad idea to use hostname. - # Hostname -> ipaddress + # Hostname -> ip address try: self.host = gethostbyname(self.host) - except Exception as e: - self.error(str(e)) + except gaierror as error: + self.error(str(error)) return False - # HTTP Auth? NOT TESTED - self.auth = self.user and self.password - + scheme = 'http' if self.scheme else 'https' + # Add handlers (auth, self signed cert accept) + self.url = '%s://%s:%s' % (scheme, self.host, self.port) + self._UrlService__add_openers() # Create URL for every Elasticsearch API - url_node_stats = 'http://%s:%s/_nodes/_local/stats' % (self.host, self.port) - url_cluster_health = 'http://%s:%s/_cluster/health' % (self.host, self.port) - url_cluster_stats = 'http://%s:%s/_cluster/stats' % (self.host, self.port) + url_node_stats = '%s://%s:%s/_nodes/_local/stats' % (scheme, self.host, self.port) + url_cluster_health = '%s://%s:%s/_cluster/health' % (scheme, self.host, self.port) + url_cluster_stats = '%s://%s:%s/_cluster/stats' % (scheme, self.host, self.port) # Create list of enabled API calls user_choice = [bool(self.configuration.get('node_stats', True)), bool(self.configuration.get('cluster_health', True)), bool(self.configuration.get('cluster_stats', True))] - - avail_methods = [(self._get_node_stats, url_node_stats), - (self._get_cluster_health, url_cluster_health), - (self._get_cluster_stats, url_cluster_stats)] + + avail_methods = [METHODS(get_data_function=self._get_node_stats_, url=url_node_stats), + METHODS(get_data_function=self._get_cluster_health_, url=url_cluster_health), + METHODS(get_data_function=self._get_cluster_stats_, url=url_cluster_stats)] # Remove disabled API calls from 'avail methods' - self.methods = [avail_methods[_] for _ in range(len(avail_methods)) if user_choice[_]] + self.methods = [avail_methods[e[0]] for e in enumerate(avail_methods) if user_choice[e[0]]] # Run _get_data for ALL active API calls. - api_result = {} + api_check_result = dict() + data_from_check = dict() for method in self.methods: - api_result[method[1]] = (bool(self._get_raw_data(method[1]))) + try: + api_check_result[method.url] = method.get_data_function(None, method.url) + data_from_check.update(api_check_result[method.url] or dict()) + except KeyError as error: + self.error('Failed to parse %s. Error: %s' % (method.url, str(error))) + return False # We can start ONLY if all active API calls returned NOT None - if not all(api_result.values()): + if not all(api_check_result.values()): self.error('Plugin could not get data from all APIs') - self.error('%s' % api_result) return False else: - self.info('%s' % api_result) - self.info('Plugin was started successfully') - + self._data_from_check = data_from_check return True - def _get_raw_data(self, url): - try: - if not self.auth: - raw_data = get(url) - else: - raw_data = get(url, auth=(self.user, self.password)) - except Exception: - return None - - return raw_data - def _get_data(self): threads = list() queue = Queue() result = dict() for method in self.methods: - th = Thread(target=method[0], args=(queue, method[1])) + th = Thread(target=method.get_data_function, args=(queue, method.url)) th.start() threads.append(th) @@ -289,102 +369,79 @@ class Service(UrlService): return result or None - def _get_cluster_health(self, queue, url): + def _get_cluster_health_(self, queue, url): """ Format data received from http request :return: dict """ - data = self._get_raw_data(url) + raw_data = self._get_raw_data(url) - if not data: - queue.put({}) + if not raw_data: + return queue.put(dict()) if queue else None else: - data = data.json() + data = loads(raw_data) + + to_netdata = fetch_data_(raw_data=data, metrics_list=HEALTH_STATS) - to_netdata = dict() - to_netdata.update(update_key('health', data)) to_netdata.update({'status_green': 0, 'status_red': 0, 'status_yellow': 0, 'status_foo1': 0, 'status_foo2': 0, 'status_foo3': 0}) - to_netdata[''.join(['status_', to_netdata.get('health_status', '')])] = 1 + current_status = 'status_' + data['status'] + to_netdata[current_status] = 1 - queue.put(to_netdata) + return queue.put(to_netdata) if queue else to_netdata - def _get_cluster_stats(self, queue, url): + def _get_cluster_stats_(self, queue, url): """ Format data received from http request :return: dict """ - data = self._get_raw_data(url) + raw_data = self._get_raw_data(url) - if not data: - queue.put({}) + if not raw_data: + return queue.put(dict()) if queue else None else: - data = data.json() + data = loads(raw_data) - to_netdata = dict() - to_netdata.update(update_key('count', data['nodes']['count'])) - to_netdata.update(update_key('query_cache', data['indices']['query_cache'])) - to_netdata.update(update_key('docs', data['indices']['docs'])) - to_netdata.update(update_key('store', data['indices']['store'])) - to_netdata['indices_count'] = data['indices']['count'] - to_netdata['shards_total'] = data['indices']['shards']['total'] + to_netdata = fetch_data_(raw_data=data, metrics_list=CLUSTER_STATS) - queue.put(to_netdata) + return queue.put(to_netdata) if queue else to_netdata - def _get_node_stats(self, queue, url): + def _get_node_stats_(self, queue, url): """ Format data received from http request :return: dict """ - data = self._get_raw_data(url) + raw_data = self._get_raw_data(url) - if not data: - queue.put({}) + if not raw_data: + return queue.put(dict()) if queue else None else: - data = data.json() + data = loads(raw_data) + node = list(data['nodes'].keys())[0] - to_netdata = dict() - # Search performance metrics - to_netdata.update(data['nodes'][node]['indices']['search']) - to_netdata['query_latency'] = self.find_avg(to_netdata['query_total'], - to_netdata['query_time_in_millis'], 'query_latency') - to_netdata['fetch_latency'] = self.find_avg(to_netdata['fetch_total'], - to_netdata['fetch_time_in_millis'], 'fetch_latency') - - # Indexing performance metrics - for key in ['indexing', 'refresh', 'flush']: - to_netdata.update(update_key(key, data['nodes'][node]['indices'].get(key, {}))) - to_netdata['indexing_latency'] = self.find_avg(to_netdata['indexing_index_total'], - to_netdata['indexing_index_time_in_millis'], 'index_latency') - to_netdata['flushing_latency'] = self.find_avg(to_netdata['flush_total'], - to_netdata['flush_total_time_in_millis'], 'flush_latency') - # Memory usage and garbage collection - to_netdata.update(update_key('young', data['nodes'][node]['jvm']['gc']['collectors']['young'])) - to_netdata.update(update_key('old', data['nodes'][node]['jvm']['gc']['collectors']['old'])) - to_netdata['jvm_heap_percent'] = data['nodes'][node]['jvm']['mem']['heap_used_percent'] - to_netdata['jvm_heap_commit'] = data['nodes'][node]['jvm']['mem']['heap_committed_in_bytes'] - - # Thread pool queues and rejections - for key in ['bulk', 'index', 'search', 'merge']: - to_netdata.update(update_key(key, data['nodes'][node]['thread_pool'].get(key, {}))) - - # Fielddata cache - to_netdata['index_fdata_mem'] = data['nodes'][node]['indices']['fielddata']['memory_size_in_bytes'] - to_netdata['index_fdata_evic'] = data['nodes'][node]['indices']['fielddata']['evictions'] - to_netdata['breakers_fdata_trip'] = data['nodes'][node]['breakers']['fielddata']['tripped'] - - # Host metrics - to_netdata.update(update_key('http', data['nodes'][node]['http'])) - to_netdata.update(update_key('transport', data['nodes'][node]['transport'])) - to_netdata['file_descriptors_used'] = round(float(data['nodes'][node]['process']['open_file_descriptors']) - / data['nodes'][node]['process']['max_file_descriptors'] * 1000) - - queue.put(to_netdata) - - def find_avg(self, value1, value2, key): + to_netdata = fetch_data_(raw_data=data['nodes'][node], metrics_list=NODE_STATS) + + # Search performance latency + to_netdata['query_latency'] = self.find_avg_(to_netdata['query_total'], + to_netdata['query_time_in_millis'], 'query_latency') + to_netdata['fetch_latency'] = self.find_avg_(to_netdata['fetch_total'], + to_netdata['fetch_time_in_millis'], 'fetch_latency') + + # Indexing performance latency + to_netdata['indexing_latency'] = self.find_avg_(to_netdata['indexing_index_total'], + to_netdata['indexing_index_time_in_millis'], 'index_latency') + to_netdata['flushing_latency'] = self.find_avg_(to_netdata['flush_total'], + to_netdata['flush_total_time_in_millis'], 'flush_latency') + + to_netdata['file_descriptors_used'] = round(float(to_netdata['open_file_descriptors']) + / to_netdata['max_file_descriptors'] * 1000) + + return queue.put(to_netdata) if queue else to_netdata + + def find_avg_(self, value1, value2, key): if key not in self.latency: self.latency.update({key: [value1, value2]}) return 0 @@ -398,5 +455,17 @@ class Service(UrlService): return 0 -def update_key(string, dictionary): - return {'_'.join([string, k]): v for k, v in dictionary.items()} +def fetch_data_(raw_data, metrics_list): + to_netdata = dict() + for metric, new_name, function in metrics_list: + value = raw_data + for key in metric.split('.'): + try: + value = value[key] + except KeyError: + break + if not isinstance(value, dict) and key: + to_netdata[new_name or key] = value if not function else function(value) + + return to_netdata + diff --git a/python.d/fail2ban.chart.py b/python.d/fail2ban.chart.py index 2d80282c6..c7d24e8c1 100644 --- a/python.d/fail2ban.chart.py +++ b/python.d/fail2ban.chart.py @@ -4,16 +4,18 @@ from base import LogService from re import compile + try: from itertools import filterfalse except ImportError: from itertools import ifilterfalse as filterfalse from os import access as is_accessible, R_OK +from os.path import isdir +from glob import glob priority = 60000 retries = 60 -regex = compile(r'([A-Za-z-]+\]) enabled = ([a-z]+)') - +REGEX = compile(r'\[([A-Za-z-_]+)][^\[\]]*?(? 0) active_leases = [k for k, v in all_leases.items() if is_binding_active(all_leases[k])] # Result: {pool: number of active bindings in pool, ...} - pools_count = {pool: len([lease for lease in active_leases if is_address_in(lease, pool)]) - for pool in self.pools} + pools_count = dict([(pool, len([lease for lease in active_leases if is_address_in(lease, pool)])) + for pool in self.pools]) # Result: {pool: number of host ip addresses in pool, ...} - pools_max = {pool: (2 ** (32 - int(pool.split('/')[1])) - 2) - for pool in self.pools} + pools_max = dict([(pool, (2 ** (32 - int(pool.split('/')[1])) - 2)) + for pool in self.pools]) # Result: {pool: % utilization, ....} (percent) - pools_util = {pool:int(round(float(pools_count[pool]) / pools_max[pool] * 100, 0)) - for pool in self.pools} + pools_util = dict([(pool, int(round(float(pools_count[pool]) / pools_max[pool] * 100, 0))) + for pool in self.pools]) # Bulding dicts to send to netdata - final_count = {''.join(['le_', k]): v for k, v in pools_count.items()} - final_util = {''.join(['ut_', k]): v for k, v in pools_util.items()} - + final_count = dict([(''.join(['le_', k]), v) for k, v in pools_count.items()]) + final_util = dict([(''.join(['ut_', k]), v) for k, v in pools_util.items()]) + to_netdata = {'total': len(active_leases)} to_netdata.update({'lsize': int(stat(self.leases_path)[6] / 1024)}) to_netdata.update({'ptime': int(raw_leases[1])}) @@ -159,6 +155,7 @@ def find_lease(value): def find_ends(value): return value[2:6] != 'ends' + def return_utf(s): # python2 returns "" for simple strings # python3 returns "" for unicode strings diff --git a/python.d/mongodb.chart.py b/python.d/mongodb.chart.py new file mode 100644 index 000000000..c01bd293c --- /dev/null +++ b/python.d/mongodb.chart.py @@ -0,0 +1,672 @@ +# -*- coding: utf-8 -*- +# Description: mongodb netdata python.d module +# Author: l2isbad + +from base import SimpleService +from copy import deepcopy +from datetime import datetime +from sys import exc_info + +try: + from pymongo import MongoClient, ASCENDING, DESCENDING + from pymongo.errors import PyMongoError + PYMONGO = True +except ImportError: + PYMONGO = False + +# default module values (can be overridden per job in `config`) +# update_every = 2 +priority = 60000 +retries = 60 + +REPLSET_STATES = [ + ('1', 'primary'), + ('8', 'down'), + ('2', 'secondary'), + ('3', 'recovering'), + ('5', 'startup2'), + ('4', 'fatal'), + ('7', 'arbiter'), + ('6', 'unknown'), + ('9', 'rollback'), + ('10', 'removed'), + ('0', 'startup')] + + +def multiply_by_100(value): + return value * 100 + +DEFAULT_METRICS = [ + ('opcounters.delete', None, None), + ('opcounters.update', None, None), + ('opcounters.insert', None, None), + ('opcounters.query', None, None), + ('opcounters.getmore', None, None), + ('globalLock.activeClients.readers', 'activeClients_readers', None), + ('globalLock.activeClients.writers', 'activeClients_writers', None), + ('connections.available', 'connections_available', None), + ('connections.current', 'connections_current', None), + ('mem.mapped', None, None), + ('mem.resident', None, None), + ('mem.virtual', None, None), + ('globalLock.currentQueue.readers', 'currentQueue_readers', None), + ('globalLock.currentQueue.writers', 'currentQueue_writers', None), + ('asserts.msg', None, None), + ('asserts.regular', None, None), + ('asserts.user', None, None), + ('asserts.warning', None, None), + ('extra_info.page_faults', None, None), + ('metrics.record.moves', None, None), + ('backgroundFlushing.average_ms', None, multiply_by_100), + ('backgroundFlushing.last_ms', None, multiply_by_100), + ('backgroundFlushing.flushes', None, multiply_by_100), + ('metrics.cursor.timedOut', None, None), + ('metrics.cursor.open.total', 'cursor_total', None), + ('metrics.cursor.open.noTimeout', None, None), + ('cursors.timedOut', None, None), + ('cursors.totalOpen', 'cursor_total', None) +] + +DUR = [ + ('dur.commits', None, None), + ('dur.journaledMB', None, multiply_by_100) +] + +WIREDTIGER = [ + ('wiredTiger.concurrentTransactions.read.available', 'wiredTigerRead_available', None), + ('wiredTiger.concurrentTransactions.read.out', 'wiredTigerRead_out', None), + ('wiredTiger.concurrentTransactions.write.available', 'wiredTigerWrite_available', None), + ('wiredTiger.concurrentTransactions.write.out', 'wiredTigerWrite_out', None), + ('wiredTiger.cache.bytes currently in the cache', None, None), + ('wiredTiger.cache.tracked dirty bytes in the cache', None, None), + ('wiredTiger.cache.maximum bytes configured', None, None), + ('wiredTiger.cache.unmodified pages evicted', 'unmodified', None), + ('wiredTiger.cache.modified pages evicted', 'modified', None) +] + +TCMALLOC = [ + ('tcmalloc.generic.current_allocated_bytes', None, None), + ('tcmalloc.generic.heap_size', None, None), + ('tcmalloc.tcmalloc.central_cache_free_bytes', None, None), + ('tcmalloc.tcmalloc.current_total_thread_cache_bytes', None, None), + ('tcmalloc.tcmalloc.pageheap_free_bytes', None, None), + ('tcmalloc.tcmalloc.pageheap_unmapped_bytes', None, None), + ('tcmalloc.tcmalloc.thread_cache_free_bytes', None, None), + ('tcmalloc.tcmalloc.transfer_cache_free_bytes', None, None) +] + +COMMANDS = [ + ('metrics.commands.count.total', 'count_total', None), + ('metrics.commands.createIndexes.total', 'createIndexes_total', None), + ('metrics.commands.delete.total', 'delete_total', None), + ('metrics.commands.eval.total', 'eval_total', None), + ('metrics.commands.findAndModify.total', 'findAndModify_total', None), + ('metrics.commands.insert.total', 'insert_total', None), + ('metrics.commands.delete.total', 'delete_total', None), + ('metrics.commands.count.failed', 'count_failed', None), + ('metrics.commands.createIndexes.failed', 'createIndexes_failed', None), + ('metrics.commands.delete.failed', 'delete_failed', None), + ('metrics.commands.eval.failed', 'eval_failed', None), + ('metrics.commands.findAndModify.failed', 'findAndModify_failed', None), + ('metrics.commands.insert.failed', 'insert_failed', None), + ('metrics.commands.delete.failed', 'delete_failed', None) +] + +LOCKS = [ + ('locks.Collection.acquireCount.R', 'Collection_R', None), + ('locks.Collection.acquireCount.r', 'Collection_r', None), + ('locks.Collection.acquireCount.W', 'Collection_W', None), + ('locks.Collection.acquireCount.w', 'Collection_w', None), + ('locks.Database.acquireCount.R', 'Database_R', None), + ('locks.Database.acquireCount.r', 'Database_r', None), + ('locks.Database.acquireCount.W', 'Database_W', None), + ('locks.Database.acquireCount.w', 'Database_w', None), + ('locks.Global.acquireCount.R', 'Global_R', None), + ('locks.Global.acquireCount.r', 'Global_r', None), + ('locks.Global.acquireCount.W', 'Global_W', None), + ('locks.Global.acquireCount.w', 'Global_w', None), + ('locks.Metadata.acquireCount.R', 'Metadata_R', None), + ('locks.Metadata.acquireCount.w', 'Metadata_w', None), + ('locks.oplog.acquireCount.r', 'oplog_r', None), + ('locks.oplog.acquireCount.w', 'oplog_w', None) +] + +DBSTATS = [ + 'dataSize', + 'indexSize', + 'storageSize', + 'objects' +] + +# charts order (can be overridden if you want less charts, or different order) +ORDER = ['read_operations', 'write_operations', 'active_clients', 'journaling_transactions', + 'journaling_volume', 'background_flush_average', 'background_flush_last', 'background_flush_rate', + 'wiredtiger_read', 'wiredtiger_write', 'cursors', 'connections', 'memory', 'page_faults', + 'queued_requests', 'record_moves', 'wiredtiger_cache', 'wiredtiger_pages_evicted', 'asserts', + 'locks_collection', 'locks_database', 'locks_global', 'locks_metadata', 'locks_oplog', + 'dbstats_objects', 'tcmalloc_generic', 'tcmalloc_metrics', 'command_total_rate', 'command_failed_rate'] + +CHARTS = { + 'read_operations': { + 'options': [None, 'Received read requests', 'requests/s', 'throughput metrics', + 'mongodb.read_operations', 'line'], + 'lines': [ + ['query', None, 'incremental'], + ['getmore', None, 'incremental'] + ]}, + 'write_operations': { + 'options': [None, 'Received write requests', 'requests/s', 'throughput metrics', + 'mongodb.write_operations', 'line'], + 'lines': [ + ['insert', None, 'incremental'], + ['update', None, 'incremental'], + ['delete', None, 'incremental'] + ]}, + 'active_clients': { + 'options': [None, 'Clients with read or write operations in progress or queued', 'clients', + 'throughput metrics', 'mongodb.active_clients', 'line'], + 'lines': [ + ['activeClients_readers', 'readers', 'absolute'], + ['activeClients_writers', 'writers', 'absolute'] + ]}, + 'journaling_transactions': { + 'options': [None, 'Transactions that have been written to the journal', 'commits', + 'database performance', 'mongodb.journaling_transactions', 'line'], + 'lines': [ + ['commits', None, 'absolute'] + ]}, + 'journaling_volume': { + 'options': [None, 'Volume of data written to the journal', 'MB', 'database performance', + 'mongodb.journaling_volume', 'line'], + 'lines': [ + ['journaledMB', 'volume', 'absolute', 1, 100] + ]}, + 'background_flush_average': { + 'options': [None, 'Average time taken by flushes to execute', 'ms', 'database performance', + 'mongodb.background_flush_average', 'line'], + 'lines': [ + ['average_ms', 'time', 'absolute', 1, 100] + ]}, + 'background_flush_last': { + 'options': [None, 'Time taken by the last flush operation to execute', 'ms', 'database performance', + 'mongodb.background_flush_last', 'line'], + 'lines': [ + ['last_ms', 'time', 'absolute', 1, 100] + ]}, + 'background_flush_rate': { + 'options': [None, 'Flushes rate', 'flushes', 'database performance', 'mongodb.background_flush_rate', 'line'], + 'lines': [ + ['flushes', 'flushes', 'incremental', 1, 1] + ]}, + 'wiredtiger_read': { + 'options': [None, 'Read tickets in use and remaining', 'tickets', 'database performance', + 'mongodb.wiredtiger_read', 'stacked'], + 'lines': [ + ['wiredTigerRead_available', 'available', 'absolute', 1, 1], + ['wiredTigerRead_out', 'inuse', 'absolute', 1, 1] + ]}, + 'wiredtiger_write': { + 'options': [None, 'Write tickets in use and remaining', 'tickets', 'database performance', + 'mongodb.wiredtiger_write', 'stacked'], + 'lines': [ + ['wiredTigerWrite_available', 'available', 'absolute', 1, 1], + ['wiredTigerWrite_out', 'inuse', 'absolute', 1, 1] + ]}, + 'cursors': { + 'options': [None, 'Currently openned cursors, cursors with timeout disabled and timed out cursors', + 'cursors', 'database performance', 'mongodb.cursors', 'stacked'], + 'lines': [ + ['cursor_total', 'openned', 'absolute', 1, 1], + ['noTimeout', None, 'absolute', 1, 1], + ['timedOut', None, 'incremental', 1, 1] + ]}, + 'connections': { + 'options': [None, 'Currently connected clients and unused connections', 'connections', + 'resource utilization', 'mongodb.connections', 'stacked'], + 'lines': [ + ['connections_available', 'unused', 'absolute', 1, 1], + ['connections_current', 'connected', 'absolute', 1, 1] + ]}, + 'memory': { + 'options': [None, 'Memory metrics', 'MB', 'resource utilization', 'mongodb.memory', 'stacked'], + 'lines': [ + ['virtual', None, 'absolute', 1, 1], + ['resident', None, 'absolute', 1, 1], + ['nonmapped', None, 'absolute', 1, 1], + ['mapped', None, 'absolute', 1, 1] + ]}, + 'page_faults': { + 'options': [None, 'Number of times MongoDB had to fetch data from disk', 'request/s', + 'resource utilization', 'mongodb.page_faults', 'line'], + 'lines': [ + ['page_faults', None, 'incremental', 1, 1] + ]}, + 'queued_requests': { + 'options': [None, 'Currently queued read and wrire requests', 'requests', 'resource saturation', + 'mongodb.queued_requests', 'line'], + 'lines': [ + ['currentQueue_readers', 'readers', 'absolute', 1, 1], + ['currentQueue_writers', 'writers', 'absolute', 1, 1] + ]}, + 'record_moves': { + 'options': [None, 'Number of times documents had to be moved on-disk', 'number', + 'resource saturation', 'mongodb.record_moves', 'line'], + 'lines': [ + ['moves', None, 'incremental', 1, 1] + ]}, + 'asserts': { + 'options': [None, 'Number of message, warning, regular, corresponding to errors generated' + ' by users assertions raised', 'number', 'errors (asserts)', 'mongodb.asserts', 'line'], + 'lines': [ + ['msg', None, 'incremental', 1, 1], + ['warning', None, 'incremental', 1, 1], + ['regular', None, 'incremental', 1, 1], + ['user', None, 'incremental', 1, 1] + ]}, + 'wiredtiger_cache': { + 'options': [None, 'The percentage of the wiredTiger cache that is in use and cache with dirty bytes', + 'percent', 'resource utilization', 'mongodb.wiredtiger_cache', 'stacked'], + 'lines': [ + ['wiredTiger_percent_clean', 'inuse', 'absolute', 1, 1000], + ['wiredTiger_percent_dirty', 'dirty', 'absolute', 1, 1000] + ]}, + 'wiredtiger_pages_evicted': { + 'options': [None, 'Pages evicted from the cache', + 'pages', 'resource utilization', 'mongodb.wiredtiger_pages_evicted', 'stacked'], + 'lines': [ + ['unmodified', None, 'absolute', 1, 1], + ['modified', None, 'absolute', 1, 1] + ]}, + 'dbstats_objects': { + 'options': [None, 'Number of documents in the database among all the collections', 'documents', + 'storage size metrics', 'mongodb.dbstats_objects', 'stacked'], + 'lines': [ + ]}, + 'tcmalloc_generic': { + 'options': [None, 'Tcmalloc generic metrics', 'MB', 'tcmalloc', 'mongodb.tcmalloc_generic', 'stacked'], + 'lines': [ + ['current_allocated_bytes', 'allocated', 'absolute', 1, 1048576], + ['heap_size', 'heap_size', 'absolute', 1, 1048576] + ]}, + 'tcmalloc_metrics': { + 'options': [None, 'Tcmalloc metrics', 'KB', 'tcmalloc', 'mongodb.tcmalloc_metrics', 'stacked'], + 'lines': [ + ['central_cache_free_bytes', 'central_cache_free', 'absolute', 1, 1024], + ['current_total_thread_cache_bytes', 'current_total_thread_cache', 'absolute', 1, 1024], + ['pageheap_free_bytes', 'pageheap_free', 'absolute', 1, 1024], + ['pageheap_unmapped_bytes', 'pageheap_unmapped', 'absolute', 1, 1024], + ['thread_cache_free_bytes', 'thread_cache_free', 'absolute', 1, 1024], + ['transfer_cache_free_bytes', 'transfer_cache_free', 'absolute', 1, 1024] + ]}, + 'command_total_rate': { + 'options': [None, 'Commands total rate', 'commands/s', 'commands', 'mongodb.command_total_rate', 'stacked'], + 'lines': [ + ['count_total', 'count', 'incremental', 1, 1], + ['createIndexes_total', 'createIndexes', 'incremental', 1, 1], + ['delete_total', 'delete', 'incremental', 1, 1], + ['eval_total', 'eval', 'incremental', 1, 1], + ['findAndModify_total', 'findAndModify', 'incremental', 1, 1], + ['insert_total', 'insert', 'incremental', 1, 1], + ['update_total', 'update', 'incremental', 1, 1] + ]}, + 'command_failed_rate': { + 'options': [None, 'Commands failed rate', 'commands/s', 'commands', 'mongodb.command_failed_rate', 'stacked'], + 'lines': [ + ['count_failed', 'count', 'incremental', 1, 1], + ['createIndexes_failed', 'createIndexes', 'incremental', 1, 1], + ['delete_failed', 'delete', 'incremental', 1, 1], + ['eval_failed', 'eval', 'incremental', 1, 1], + ['findAndModify_failed', 'findAndModify', 'incremental', 1, 1], + ['insert_failed', 'insert', 'incremental', 1, 1], + ['update_failed', 'update', 'incremental', 1, 1] + ]}, + 'locks_collection': { + 'options': [None, 'Collection lock. Number of times the lock was acquired in the specified mode', + 'locks', 'locks metrics', 'mongodb.locks_collection', 'stacked'], + 'lines': [ + ['Collection_R', 'shared', 'incremental'], + ['Collection_W', 'exclusive', 'incremental'], + ['Collection_r', 'intent_shared', 'incremental'], + ['Collection_w', 'intent_exclusive', 'incremental'] + ]}, + 'locks_database': { + 'options': [None, 'Database lock. Number of times the lock was acquired in the specified mode', + 'locks', 'locks metrics', 'mongodb.locks_database', 'stacked'], + 'lines': [ + ['Database_R', 'shared', 'incremental'], + ['Database_W', 'exclusive', 'incremental'], + ['Database_r', 'intent_shared', 'incremental'], + ['Database_w', 'intent_exclusive', 'incremental'] + ]}, + 'locks_global': { + 'options': [None, 'Global lock. Number of times the lock was acquired in the specified mode', + 'locks', 'locks metrics', 'mongodb.locks_global', 'stacked'], + 'lines': [ + ['Global_R', 'shared', 'incremental'], + ['Global_W', 'exclusive', 'incremental'], + ['Global_r', 'intent_shared', 'incremental'], + ['Global_w', 'intent_exclusive', 'incremental'] + ]}, + 'locks_metadata': { + 'options': [None, 'Metadata lock. Number of times the lock was acquired in the specified mode', + 'locks', 'locks metrics', 'mongodb.locks_metadata', 'stacked'], + 'lines': [ + ['Metadata_R', 'shared', 'incremental'], + ['Metadata_w', 'intent_exclusive', 'incremental'] + ]}, + 'locks_oplog': { + 'options': [None, 'Lock on the oplog. Number of times the lock was acquired in the specified mode', + 'locks', 'locks metrics', 'mongodb.locks_oplog', 'stacked'], + 'lines': [ + ['Metadata_r', 'intent_shared', 'incremental'], + ['Metadata_w', 'intent_exclusive', 'incremental'] + ]} +} + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.order = ORDER[:] + self.definitions = deepcopy(CHARTS) + self.user = self.configuration.get('user') + self.password = self.configuration.get('pass') + self.host = self.configuration.get('host', '127.0.0.1') + self.port = self.configuration.get('port', 27017) + self.timeout = self.configuration.get('timeout', 100) + self.metrics_to_collect = deepcopy(DEFAULT_METRICS) + self.connection = None + self.do_replica = None + self.databases = list() + + def check(self): + if not PYMONGO: + self.error('Pymongo module is needed to use mongodb.chart.py') + return False + self.connection, server_status, error = self._create_connection() + if error: + self.error(error) + return False + + self.build_metrics_to_collect_(server_status) + + try: + self._get_data() + except (LookupError, SyntaxError, AttributeError): + self.error('Type: %s, error: %s' % (str(exc_info()[0]), str(exc_info()[1]))) + return False + else: + self.create_charts_(server_status) + return True + + def build_metrics_to_collect_(self, server_status): + + self.do_replica = 'repl' in server_status + if 'dur' in server_status: + self.metrics_to_collect.extend(DUR) + if 'tcmalloc' in server_status: + self.metrics_to_collect.extend(TCMALLOC) + if 'commands' in server_status['metrics']: + self.metrics_to_collect.extend(COMMANDS) + if 'wiredTiger' in server_status: + self.metrics_to_collect.extend(WIREDTIGER) + if 'Collection' in server_status['locks']: + self.metrics_to_collect.extend(LOCKS) + + def create_charts_(self, server_status): + + if 'dur' not in server_status: + self.order.remove('journaling_transactions') + self.order.remove('journaling_volume') + + if 'backgroundFlushing' not in server_status: + self.order.remove('background_flush_average') + self.order.remove('background_flush_last') + self.order.remove('background_flush_rate') + + if 'wiredTiger' not in server_status: + self.order.remove('wiredtiger_write') + self.order.remove('wiredtiger_read') + self.order.remove('wiredtiger_cache') + + if 'tcmalloc' not in server_status: + self.order.remove('tcmalloc_generic') + self.order.remove('tcmalloc_metrics') + + if 'commands' not in server_status['metrics']: + self.order.remove('command_total_rate') + self.order.remove('command_failed_rate') + + if 'Collection' not in server_status['locks']: + self.order.remove('locks_collection') + self.order.remove('locks_database') + self.order.remove('locks_global') + self.order.remove('locks_metadata') + + if 'oplog' not in server_status['locks']: + self.order.remove('locks_oplog') + + for dbase in self.databases: + self.order.append('_'.join([dbase, 'dbstats'])) + self.definitions['_'.join([dbase, 'dbstats'])] = { + 'options': [None, '%s: size of all documents, indexes, extents' % dbase, 'KB', + 'storage size metrics', 'mongodb.dbstats', 'line'], + 'lines': [ + ['_'.join([dbase, 'dataSize']), 'documents', 'absolute', 1, 1024], + ['_'.join([dbase, 'indexSize']), 'indexes', 'absolute', 1, 1024], + ['_'.join([dbase, 'storageSize']), 'extents', 'absolute', 1, 1024] + ]} + self.definitions['dbstats_objects']['lines'].append(['_'.join([dbase, 'objects']), dbase, 'absolute']) + + if self.do_replica: + def create_lines(hosts, string): + lines = list() + for host in hosts: + dim_id = '_'.join([host, string]) + lines.append([dim_id, host, 'absolute', 1, 1000]) + return lines + + def create_state_lines(states): + lines = list() + for state, description in states: + dim_id = '_'.join([host, 'state', state]) + lines.append([dim_id, description, 'absolute', 1, 1]) + return lines + + all_hosts = server_status['repl']['hosts'] + this_host = server_status['repl']['me'] + other_hosts = [host for host in all_hosts if host != this_host] + + if 'local' in self.databases: + self.order.append('oplog_window') + self.definitions['oplog_window'] = { + 'options': [None, 'Interval of time between the oldest and the latest entries in the oplog', + 'seconds', 'replication and oplog', 'mongodb.oplog_window', 'line'], + 'lines': [['timeDiff', 'window', 'absolute', 1, 1000]]} + # Create "heartbeat delay" chart + self.order.append('heartbeat_delay') + self.definitions['heartbeat_delay'] = { + 'options': [None, 'Time when last heartbeat was received' + ' from the replica set member (lastHeartbeatRecv)', + 'seconds ago', 'replication and oplog', 'mongodb.replication_heartbeat_delay', 'stacked'], + 'lines': create_lines(other_hosts, 'heartbeat_lag')} + # Create "optimedate delay" chart + self.order.append('optimedate_delay') + self.definitions['optimedate_delay'] = { + 'options': [None, 'Time when last entry from the oplog was applied (optimeDate)', + 'seconds ago', 'replication and oplog', 'mongodb.replication_optimedate_delay', 'stacked'], + 'lines': create_lines(all_hosts, 'optimedate')} + # Create "replica set members state" chart + for host in all_hosts: + chart_name = '_'.join([host, 'state']) + self.order.append(chart_name) + self.definitions[chart_name] = { + 'options': [None, 'Replica set member (%s) current state' % host, 'state', + 'replication and oplog', 'mongodb.replication_state', 'line'], + 'lines': create_state_lines(REPLSET_STATES)} + + def _get_raw_data(self): + raw_data = dict() + + raw_data.update(self.get_serverstatus_() or dict()) + raw_data.update(self.get_dbstats_() or dict()) + raw_data.update(self.get_replsetgetstatus_() or dict()) + raw_data.update(self.get_getreplicationinfo_() or dict()) + + return raw_data or None + + def get_serverstatus_(self): + raw_data = dict() + try: + raw_data['serverStatus'] = self.connection.admin.command('serverStatus') + except PyMongoError: + return None + else: + return raw_data + + def get_dbstats_(self): + if not self.databases: + return None + + raw_data = dict() + raw_data['dbStats'] = dict() + try: + for dbase in self.databases: + raw_data['dbStats'][dbase] = self.connection[dbase].command('dbStats') + except PyMongoError: + return None + else: + return raw_data + + def get_replsetgetstatus_(self): + if not self.do_replica: + return None + + raw_data = dict() + try: + raw_data['replSetGetStatus'] = self.connection.admin.command('replSetGetStatus') + except PyMongoError: + return None + else: + return raw_data + + def get_getreplicationinfo_(self): + if not (self.do_replica and 'local' in self.databases): + return None + + raw_data = dict() + raw_data['getReplicationInfo'] = dict() + try: + raw_data['getReplicationInfo']['ASCENDING'] = self.connection.local.oplog.rs.find().sort( + "$natural", ASCENDING).limit(1)[0] + raw_data['getReplicationInfo']['DESCENDING'] = self.connection.local.oplog.rs.find().sort( + "$natural", DESCENDING).limit(1)[0] + except PyMongoError: + return None + else: + return raw_data + + def _get_data(self): + """ + :return: dict + """ + raw_data = self._get_raw_data() + + if not raw_data: + return None + + to_netdata = dict() + serverStatus = raw_data['serverStatus'] + dbStats = raw_data.get('dbStats') + replSetGetStatus = raw_data.get('replSetGetStatus') + getReplicationInfo = raw_data.get('getReplicationInfo') + utc_now = datetime.utcnow() + + # serverStatus + for metric, new_name, function in self.metrics_to_collect: + value = serverStatus + for key in metric.split('.'): + try: + value = value[key] + except KeyError: + break + + if not isinstance(value, dict) and key: + to_netdata[new_name or key] = value if not function else function(value) + + to_netdata['nonmapped'] = to_netdata['virtual'] - serverStatus['mem'].get('mappedWithJournal', + to_netdata['mapped']) + if to_netdata.get('maximum bytes configured'): + maximum = to_netdata['maximum bytes configured'] + to_netdata['wiredTiger_percent_clean'] = int(to_netdata['bytes currently in the cache'] + * 100 / maximum * 1000) + to_netdata['wiredTiger_percent_dirty'] = int(to_netdata['tracked dirty bytes in the cache'] + * 100 / maximum * 1000) + + # dbStats + if dbStats: + for dbase in dbStats: + for metric in DBSTATS: + key = '_'.join([dbase, metric]) + to_netdata[key] = dbStats[dbase][metric] + + # replSetGetStatus + if replSetGetStatus: + other_hosts = list() + members = replSetGetStatus['members'] + unix_epoch = datetime(1970, 1, 1, 0, 0) + + for member in members: + if not member.get('self'): + other_hosts.append(member) + # Replica set time diff between current time and time when last entry from the oplog was applied + if member['optimeDate'] != unix_epoch: + member_optimedate = member['name'] + '_optimedate' + to_netdata.update({member_optimedate: int(delta_calculation(delta=utc_now - member['optimeDate'], + multiplier=1000))}) + # Replica set members state + member_state = member['name'] + '_state' + for elem in REPLSET_STATES: + state = elem[0] + to_netdata.update({'_'.join([member_state, state]): 0}) + to_netdata.update({'_'.join([member_state, str(member['state'])]): member['state']}) + # Heartbeat lag calculation + for other in other_hosts: + if other['lastHeartbeatRecv'] != unix_epoch: + node = other['name'] + '_heartbeat_lag' + to_netdata[node] = int(delta_calculation(delta=utc_now - other['lastHeartbeatRecv'], + multiplier=1000)) + + if getReplicationInfo: + first_event = getReplicationInfo['ASCENDING']['ts'].as_datetime() + last_event = getReplicationInfo['DESCENDING']['ts'].as_datetime() + to_netdata['timeDiff'] = int(delta_calculation(delta=last_event - first_event, multiplier=1000)) + + return to_netdata + + def _create_connection(self): + conn_vars = {'host': self.host, 'port': self.port} + if hasattr(MongoClient, 'server_selection_timeout'): + conn_vars.update({'serverselectiontimeoutms': self.timeout}) + try: + connection = MongoClient(**conn_vars) + if self.user and self.password: + connection.admin.authenticate(name=self.user, password=self.password) + # elif self.user: + # connection.admin.authenticate(name=self.user, mechanism='MONGODB-X509') + server_status = connection.admin.command('serverStatus') + except PyMongoError as error: + return None, None, str(error) + else: + try: + self.databases = connection.database_names() + except PyMongoError as error: + self.info('Can\'t collect databases: %s' % str(error)) + return connection, server_status, None + + +def delta_calculation(delta, multiplier=1): + if hasattr(delta, 'total_seconds'): + return delta.total_seconds() * multiplier + else: + return (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6) / 10.0 ** 6 * multiplier diff --git a/python.d/mysql.chart.py b/python.d/mysql.chart.py index 0e3a03299..abf6bf715 100644 --- a/python.d/mysql.chart.py +++ b/python.d/mysql.chart.py @@ -2,45 +2,132 @@ # Description: MySQL netdata python.d module # Author: Pawel Krupa (paulfantom) -from base import SimpleService -import msg - -# import 3rd party library to handle MySQL communication -try: - import MySQLdb - - # https://github.com/PyMySQL/mysqlclient-python - msg.info("using MySQLdb") -except ImportError: - try: - import pymysql as MySQLdb - - # https://github.com/PyMySQL/PyMySQL - msg.info("using pymysql") - except ImportError: - msg.error("MySQLdb or PyMySQL module is needed to use mysql.chart.py plugin") - raise ImportError +from base import MySQLService # default module values (can be overridden per job in `config`) # update_every = 3 priority = 90000 retries = 60 -# default configuration (overridden by python.d.plugin) -# config = { -# 'local': { -# 'user': 'root', -# 'pass': '', -# 'socket': '/var/run/mysqld/mysqld.sock', -# 'update_every': update_every, -# 'retries': retries, -# 'priority': priority -# } -#} - # query executed on MySQL server -QUERY = "SHOW GLOBAL STATUS;" -QUERY_SLAVE = "SHOW SLAVE STATUS;" +QUERY_GLOBAL = 'SHOW GLOBAL STATUS;' +QUERY_SLAVE = 'SHOW SLAVE STATUS;' + +GLOBAL_STATS = [ + 'Bytes_received', + 'Bytes_sent', + 'Queries', + 'Questions', + 'Slow_queries', + 'Handler_commit', + 'Handler_delete', + 'Handler_prepare', + 'Handler_read_first', + 'Handler_read_key', + 'Handler_read_next', + 'Handler_read_prev', + 'Handler_read_rnd', + 'Handler_read_rnd_next', + 'Handler_rollback', + 'Handler_savepoint', + 'Handler_savepoint_rollback', + 'Handler_update', + 'Handler_write', + 'Table_locks_immediate', + 'Table_locks_waited', + 'Select_full_join', + 'Select_full_range_join', + 'Select_range', + 'Select_range_check', + 'Select_scan', + 'Sort_merge_passes', + 'Sort_range', + 'Sort_scan', + 'Created_tmp_disk_tables', + 'Created_tmp_files', + 'Created_tmp_tables', + 'Connections', + 'Aborted_connects', + 'Binlog_cache_disk_use', + 'Binlog_cache_use', + 'Threads_connected', + 'Threads_created', + 'Threads_cached', + 'Threads_running', + 'Thread_cache_misses', + 'Innodb_data_read', + 'Innodb_data_written', + 'Innodb_data_reads', + 'Innodb_data_writes', + 'Innodb_data_fsyncs', + 'Innodb_data_pending_reads', + 'Innodb_data_pending_writes', + 'Innodb_data_pending_fsyncs', + 'Innodb_log_waits', + 'Innodb_log_write_requests', + 'Innodb_log_writes', + 'Innodb_os_log_fsyncs', + 'Innodb_os_log_pending_fsyncs', + 'Innodb_os_log_pending_writes', + 'Innodb_os_log_written', + 'Innodb_row_lock_current_waits', + 'Innodb_rows_inserted', + 'Innodb_rows_read', + 'Innodb_rows_updated', + 'Innodb_rows_deleted', + 'Innodb_buffer_pool_pages_data', + 'Innodb_buffer_pool_pages_dirty', + 'Innodb_buffer_pool_pages_free', + 'Innodb_buffer_pool_pages_flushed', + 'Innodb_buffer_pool_pages_misc', + 'Innodb_buffer_pool_pages_total', + 'Innodb_buffer_pool_bytes_data', + 'Innodb_buffer_pool_bytes_dirty', + 'Innodb_buffer_pool_read_ahead', + 'Innodb_buffer_pool_read_ahead_evicted', + 'Innodb_buffer_pool_read_ahead_rnd', + 'Innodb_buffer_pool_read_requests', + 'Innodb_buffer_pool_write_requests', + 'Innodb_buffer_pool_reads', + 'Innodb_buffer_pool_wait_free', + 'Qcache_hits', + 'Qcache_lowmem_prunes', + 'Qcache_inserts', + 'Qcache_not_cached', + 'Qcache_queries_in_cache', + 'Qcache_free_memory', + 'Qcache_free_blocks', + 'Qcache_total_blocks', + 'Key_blocks_unused', + 'Key_blocks_used', + 'Key_blocks_not_flushed', + 'Key_read_requests', + 'Key_write_requests', + 'Key_reads', + 'Key_writes', + 'Open_files', + 'Opened_files', + 'Binlog_stmt_cache_disk_use', + 'Binlog_stmt_cache_use', + 'Connection_errors_accept', + 'Connection_errors_internal', + 'Connection_errors_max_connections', + 'Connection_errors_peer_address', + 'Connection_errors_select', + 'Connection_errors_tcpwrap'] + +def slave_seconds(value): + return value if value else -1 + +def slave_running(value): + return 1 if value == 'Yes' else -1 + + +SLAVE_STATS = [ + ('Seconds_Behind_Master', slave_seconds), + ('Slave_SQL_Running', slave_running), + ('Slave_IO_Running', slave_running) +] ORDER = ['net', 'queries', @@ -62,431 +149,290 @@ CHARTS = { 'net': { 'options': [None, 'mysql Bandwidth', 'kilobits/s', 'bandwidth', 'mysql.net', 'area'], 'lines': [ - ["Bytes_received", "in", "incremental", 8, 1024], - ["Bytes_sent", "out", "incremental", -8, 1024] + ['Bytes_received', 'in', 'incremental', 8, 1024], + ['Bytes_sent', 'out', 'incremental', -8, 1024] ]}, 'queries': { 'options': [None, 'mysql Queries', 'queries/s', 'queries', 'mysql.queries', 'line'], 'lines': [ - ["Queries", "queries", "incremental"], - ["Questions", "questions", "incremental"], - ["Slow_queries", "slow_queries", "incremental"] + ['Queries', 'queries', 'incremental'], + ['Questions', 'questions', 'incremental'], + ['Slow_queries', 'slow_queries', 'incremental'] ]}, 'handlers': { 'options': [None, 'mysql Handlers', 'handlers/s', 'handlers', 'mysql.handlers', 'line'], 'lines': [ - ["Handler_commit", "commit", "incremental"], - ["Handler_delete", "delete", "incremental"], - ["Handler_prepare", "prepare", "incremental"], - ["Handler_read_first", "read_first", "incremental"], - ["Handler_read_key", "read_key", "incremental"], - ["Handler_read_next", "read_next", "incremental"], - ["Handler_read_prev", "read_prev", "incremental"], - ["Handler_read_rnd", "read_rnd", "incremental"], - ["Handler_read_rnd_next", "read_rnd_next", "incremental"], - ["Handler_rollback", "rollback", "incremental"], - ["Handler_savepoint", "savepoint", "incremental"], - ["Handler_savepoint_rollback", "savepoint_rollback", "incremental"], - ["Handler_update", "update", "incremental"], - ["Handler_write", "write", "incremental"] + ['Handler_commit', 'commit', 'incremental'], + ['Handler_delete', 'delete', 'incremental'], + ['Handler_prepare', 'prepare', 'incremental'], + ['Handler_read_first', 'read_first', 'incremental'], + ['Handler_read_key', 'read_key', 'incremental'], + ['Handler_read_next', 'read_next', 'incremental'], + ['Handler_read_prev', 'read_prev', 'incremental'], + ['Handler_read_rnd', 'read_rnd', 'incremental'], + ['Handler_read_rnd_next', 'read_rnd_next', 'incremental'], + ['Handler_rollback', 'rollback', 'incremental'], + ['Handler_savepoint', 'savepoint', 'incremental'], + ['Handler_savepoint_rollback', 'savepoint_rollback', 'incremental'], + ['Handler_update', 'update', 'incremental'], + ['Handler_write', 'write', 'incremental'] ]}, 'table_locks': { 'options': [None, 'mysql Tables Locks', 'locks/s', 'locks', 'mysql.table_locks', 'line'], 'lines': [ - ["Table_locks_immediate", "immediate", "incremental"], - ["Table_locks_waited", "waited", "incremental", -1, 1] + ['Table_locks_immediate', 'immediate', 'incremental'], + ['Table_locks_waited', 'waited', 'incremental', -1, 1] ]}, 'join_issues': { 'options': [None, 'mysql Select Join Issues', 'joins/s', 'issues', 'mysql.join_issues', 'line'], 'lines': [ - ["Select_full_join", "full_join", "incremental"], - ["Select_full_range_join", "full_range_join", "incremental"], - ["Select_range", "range", "incremental"], - ["Select_range_check", "range_check", "incremental"], - ["Select_scan", "scan", "incremental"] + ['Select_full_join', 'full_join', 'incremental'], + ['Select_full_range_join', 'full_range_join', 'incremental'], + ['Select_range', 'range', 'incremental'], + ['Select_range_check', 'range_check', 'incremental'], + ['Select_scan', 'scan', 'incremental'] ]}, 'sort_issues': { 'options': [None, 'mysql Sort Issues', 'issues/s', 'issues', 'mysql.sort_issues', 'line'], 'lines': [ - ["Sort_merge_passes", "merge_passes", "incremental"], - ["Sort_range", "range", "incremental"], - ["Sort_scan", "scan", "incremental"] + ['Sort_merge_passes', 'merge_passes', 'incremental'], + ['Sort_range', 'range', 'incremental'], + ['Sort_scan', 'scan', 'incremental'] ]}, 'tmp': { 'options': [None, 'mysql Tmp Operations', 'counter', 'temporaries', 'mysql.tmp', 'line'], 'lines': [ - ["Created_tmp_disk_tables", "disk_tables", "incremental"], - ["Created_tmp_files", "files", "incremental"], - ["Created_tmp_tables", "tables", "incremental"] + ['Created_tmp_disk_tables', 'disk_tables', 'incremental'], + ['Created_tmp_files', 'files', 'incremental'], + ['Created_tmp_tables', 'tables', 'incremental'] ]}, 'connections': { 'options': [None, 'mysql Connections', 'connections/s', 'connections', 'mysql.connections', 'line'], 'lines': [ - ["Connections", "all", "incremental"], - ["Aborted_connects", "aborted", "incremental"] + ['Connections', 'all', 'incremental'], + ['Aborted_connects', 'aborted', 'incremental'] ]}, 'binlog_cache': { 'options': [None, 'mysql Binlog Cache', 'transactions/s', 'binlog', 'mysql.binlog_cache', 'line'], 'lines': [ - ["Binlog_cache_disk_use", "disk", "incremental"], - ["Binlog_cache_use", "all", "incremental"] + ['Binlog_cache_disk_use', 'disk', 'incremental'], + ['Binlog_cache_use', 'all', 'incremental'] ]}, 'threads': { 'options': [None, 'mysql Threads', 'threads', 'threads', 'mysql.threads', 'line'], 'lines': [ - ["Threads_connected", "connected", "absolute"], - ["Threads_created", "created", "incremental"], - ["Threads_cached", "cached", "absolute", -1, 1], - ["Threads_running", "running", "absolute"], + ['Threads_connected', 'connected', 'absolute'], + ['Threads_created', 'created', 'incremental'], + ['Threads_cached', 'cached', 'absolute', -1, 1], + ['Threads_running', 'running', 'absolute'], ]}, 'thread_cache_misses': { 'options': [None, 'mysql Threads Cache Misses', 'misses', 'threads', 'mysql.thread_cache_misses', 'area'], 'lines': [ - ["Thread_cache_misses", "misses", "absolute", 1, 100] + ['Thread_cache_misses', 'misses', 'absolute', 1, 100] ]}, 'innodb_io': { 'options': [None, 'mysql InnoDB I/O Bandwidth', 'kilobytes/s', 'innodb', 'mysql.innodb_io', 'area'], 'lines': [ - ["Innodb_data_read", "read", "incremental", 1, 1024], - ["Innodb_data_written", "write", "incremental", -1, 1024] + ['Innodb_data_read', 'read', 'incremental', 1, 1024], + ['Innodb_data_written', 'write', 'incremental', -1, 1024] ]}, 'innodb_io_ops': { 'options': [None, 'mysql InnoDB I/O Operations', 'operations/s', 'innodb', 'mysql.innodb_io_ops', 'line'], 'lines': [ - ["Innodb_data_reads", "reads", "incremental"], - ["Innodb_data_writes", "writes", "incremental", -1, 1], - ["Innodb_data_fsyncs", "fsyncs", "incremental"] + ['Innodb_data_reads', 'reads', 'incremental'], + ['Innodb_data_writes', 'writes', 'incremental', -1, 1], + ['Innodb_data_fsyncs', 'fsyncs', 'incremental'] ]}, 'innodb_io_pending_ops': { 'options': [None, 'mysql InnoDB Pending I/O Operations', 'operations', 'innodb', 'mysql.innodb_io_pending_ops', 'line'], 'lines': [ - ["Innodb_data_pending_reads", "reads", "absolute"], - ["Innodb_data_pending_writes", "writes", "absolute", -1, 1], - ["Innodb_data_pending_fsyncs", "fsyncs", "absolute"] + ['Innodb_data_pending_reads', 'reads', 'absolute'], + ['Innodb_data_pending_writes', 'writes', 'absolute', -1, 1], + ['Innodb_data_pending_fsyncs', 'fsyncs', 'absolute'] ]}, 'innodb_log': { 'options': [None, 'mysql InnoDB Log Operations', 'operations/s', 'innodb', 'mysql.innodb_log', 'line'], 'lines': [ - ["Innodb_log_waits", "waits", "incremental"], - ["Innodb_log_write_requests", "write_requests", "incremental", -1, 1], - ["Innodb_log_writes", "writes", "incremental", -1, 1], + ['Innodb_log_waits', 'waits', 'incremental'], + ['Innodb_log_write_requests', 'write_requests', 'incremental', -1, 1], + ['Innodb_log_writes', 'writes', 'incremental', -1, 1], ]}, 'innodb_os_log': { 'options': [None, 'mysql InnoDB OS Log Operations', 'operations', 'innodb', 'mysql.innodb_os_log', 'line'], 'lines': [ - ["Innodb_os_log_fsyncs", "fsyncs", "incremental"], - ["Innodb_os_log_pending_fsyncs", "pending_fsyncs", "absolute"], - ["Innodb_os_log_pending_writes", "pending_writes", "absolute", -1, 1], + ['Innodb_os_log_fsyncs', 'fsyncs', 'incremental'], + ['Innodb_os_log_pending_fsyncs', 'pending_fsyncs', 'absolute'], + ['Innodb_os_log_pending_writes', 'pending_writes', 'absolute', -1, 1], ]}, 'innodb_os_log_io': { 'options': [None, 'mysql InnoDB OS Log Bandwidth', 'kilobytes/s', 'innodb', 'mysql.innodb_os_log_io', 'area'], 'lines': [ - ["Innodb_os_log_written", "write", "incremental", -1, 1024], + ['Innodb_os_log_written', 'write', 'incremental', -1, 1024], ]}, 'innodb_cur_row_lock': { 'options': [None, 'mysql InnoDB Current Row Locks', 'operations', 'innodb', 'mysql.innodb_cur_row_lock', 'area'], 'lines': [ - ["Innodb_row_lock_current_waits", "current_waits", "absolute"] + ['Innodb_row_lock_current_waits', 'current_waits', 'absolute'] ]}, 'innodb_rows': { 'options': [None, 'mysql InnoDB Row Operations', 'operations/s', 'innodb', 'mysql.innodb_rows', 'area'], 'lines': [ - ["Innodb_rows_inserted", "read", "incremental"], - ["Innodb_rows_read", "deleted", "incremental", -1, 1], - ["Innodb_rows_updated", "inserted", "incremental", 1, 1], - ["Innodb_rows_deleted", "updated", "incremental", -1, 1], + ['Innodb_rows_inserted', 'read', 'incremental'], + ['Innodb_rows_read', 'deleted', 'incremental', -1, 1], + ['Innodb_rows_updated', 'inserted', 'incremental', 1, 1], + ['Innodb_rows_deleted', 'updated', 'incremental', -1, 1], ]}, 'innodb_buffer_pool_pages': { 'options': [None, 'mysql InnoDB Buffer Pool Pages', 'pages', 'innodb', 'mysql.innodb_buffer_pool_pages', 'line'], 'lines': [ - ["Innodb_buffer_pool_pages_data", "data", "absolute"], - ["Innodb_buffer_pool_pages_dirty", "dirty", "absolute", -1, 1], - ["Innodb_buffer_pool_pages_free", "free", "absolute"], - ["Innodb_buffer_pool_pages_flushed", "flushed", "incremental", -1, 1], - ["Innodb_buffer_pool_pages_misc", "misc", "absolute", -1, 1], - ["Innodb_buffer_pool_pages_total", "total", "absolute"] + ['Innodb_buffer_pool_pages_data', 'data', 'absolute'], + ['Innodb_buffer_pool_pages_dirty', 'dirty', 'absolute', -1, 1], + ['Innodb_buffer_pool_pages_free', 'free', 'absolute'], + ['Innodb_buffer_pool_pages_flushed', 'flushed', 'incremental', -1, 1], + ['Innodb_buffer_pool_pages_misc', 'misc', 'absolute', -1, 1], + ['Innodb_buffer_pool_pages_total', 'total', 'absolute'] ]}, 'innodb_buffer_pool_bytes': { 'options': [None, 'mysql InnoDB Buffer Pool Bytes', 'MB', 'innodb', 'mysql.innodb_buffer_pool_bytes', 'area'], 'lines': [ - ["Innodb_buffer_pool_bytes_data", "data", "absolute", 1, 1024 * 1024], - ["Innodb_buffer_pool_bytes_dirty", "dirty", "absolute", -1, 1024 * 1024] + ['Innodb_buffer_pool_bytes_data', 'data', 'absolute', 1, 1024 * 1024], + ['Innodb_buffer_pool_bytes_dirty', 'dirty', 'absolute', -1, 1024 * 1024] ]}, 'innodb_buffer_pool_read_ahead': { 'options': [None, 'mysql InnoDB Buffer Pool Read Ahead', 'operations/s', 'innodb', 'mysql.innodb_buffer_pool_read_ahead', 'area'], 'lines': [ - ["Innodb_buffer_pool_read_ahead", "all", "incremental"], - ["Innodb_buffer_pool_read_ahead_evicted", "evicted", "incremental", -1, 1], - ["Innodb_buffer_pool_read_ahead_rnd", "random", "incremental"] + ['Innodb_buffer_pool_read_ahead', 'all', 'incremental'], + ['Innodb_buffer_pool_read_ahead_evicted', 'evicted', 'incremental', -1, 1], + ['Innodb_buffer_pool_read_ahead_rnd', 'random', 'incremental'] ]}, 'innodb_buffer_pool_reqs': { 'options': [None, 'mysql InnoDB Buffer Pool Requests', 'requests/s', 'innodb', 'mysql.innodb_buffer_pool_reqs', 'area'], 'lines': [ - ["Innodb_buffer_pool_read_requests", "reads", "incremental"], - ["Innodb_buffer_pool_write_requests", "writes", "incremental", -1, 1] + ['Innodb_buffer_pool_read_requests', 'reads', 'incremental'], + ['Innodb_buffer_pool_write_requests', 'writes', 'incremental', -1, 1] ]}, 'innodb_buffer_pool_ops': { 'options': [None, 'mysql InnoDB Buffer Pool Operations', 'operations/s', 'innodb', 'mysql.innodb_buffer_pool_ops', 'area'], 'lines': [ - ["Innodb_buffer_pool_reads", "disk reads", "incremental"], - ["Innodb_buffer_pool_wait_free", "wait free", "incremental", -1, 1] + ['Innodb_buffer_pool_reads', 'disk reads', 'incremental'], + ['Innodb_buffer_pool_wait_free', 'wait free', 'incremental', -1, 1] ]}, 'qcache_ops': { 'options': [None, 'mysql QCache Operations', 'queries/s', 'qcache', 'mysql.qcache_ops', 'line'], 'lines': [ - ["Qcache_hits", "hits", "incremental"], - ["Qcache_lowmem_prunes", "lowmem prunes", "incremental", -1, 1], - ["Qcache_inserts", "inserts", "incremental"], - ["Qcache_not_cached", "not cached", "incremental", -1, 1] + ['Qcache_hits', 'hits', 'incremental'], + ['Qcache_lowmem_prunes', 'lowmem prunes', 'incremental', -1, 1], + ['Qcache_inserts', 'inserts', 'incremental'], + ['Qcache_not_cached', 'not cached', 'incremental', -1, 1] ]}, 'qcache': { 'options': [None, 'mysql QCache Queries in Cache', 'queries', 'qcache', 'mysql.qcache', 'line'], 'lines': [ - ["Qcache_queries_in_cache", "queries", "absolute"] + ['Qcache_queries_in_cache', 'queries', 'absolute'] ]}, 'qcache_freemem': { 'options': [None, 'mysql QCache Free Memory', 'MB', 'qcache', 'mysql.qcache_freemem', 'area'], 'lines': [ - ["Qcache_free_memory", "free", "absolute", 1, 1024 * 1024] + ['Qcache_free_memory', 'free', 'absolute', 1, 1024 * 1024] ]}, 'qcache_memblocks': { 'options': [None, 'mysql QCache Memory Blocks', 'blocks', 'qcache', 'mysql.qcache_memblocks', 'line'], 'lines': [ - ["Qcache_free_blocks", "free", "absolute"], - ["Qcache_total_blocks", "total", "absolute"] + ['Qcache_free_blocks', 'free', 'absolute'], + ['Qcache_total_blocks', 'total', 'absolute'] ]}, 'key_blocks': { 'options': [None, 'mysql MyISAM Key Cache Blocks', 'blocks', 'myisam', 'mysql.key_blocks', 'line'], 'lines': [ - ["Key_blocks_unused", "unused", "absolute"], - ["Key_blocks_used", "used", "absolute", -1, 1], - ["Key_blocks_not_flushed", "not flushed", "absolute"] + ['Key_blocks_unused', 'unused', 'absolute'], + ['Key_blocks_used', 'used', 'absolute', -1, 1], + ['Key_blocks_not_flushed', 'not flushed', 'absolute'] ]}, 'key_requests': { 'options': [None, 'mysql MyISAM Key Cache Requests', 'requests/s', 'myisam', 'mysql.key_requests', 'area'], 'lines': [ - ["Key_read_requests", "reads", "incremental"], - ["Key_write_requests", "writes", "incremental", -1, 1] + ['Key_read_requests', 'reads', 'incremental'], + ['Key_write_requests', 'writes', 'incremental', -1, 1] ]}, 'key_disk_ops': { 'options': [None, 'mysql MyISAM Key Cache Disk Operations', 'operations/s', 'myisam', 'mysql.key_disk_ops', 'area'], 'lines': [ - ["Key_reads", "reads", "incremental"], - ["Key_writes", "writes", "incremental", -1, 1] + ['Key_reads', 'reads', 'incremental'], + ['Key_writes', 'writes', 'incremental', -1, 1] ]}, 'files': { 'options': [None, 'mysql Open Files', 'files', 'files', 'mysql.files', 'line'], 'lines': [ - ["Open_files", "files", "absolute"] + ['Open_files', 'files', 'absolute'] ]}, 'files_rate': { 'options': [None, 'mysql Opened Files Rate', 'files/s', 'files', 'mysql.files_rate', 'line'], 'lines': [ - ["Opened_files", "files", "incremental"] + ['Opened_files', 'files', 'incremental'] ]}, 'binlog_stmt_cache': { 'options': [None, 'mysql Binlog Statement Cache', 'statements/s', 'binlog', 'mysql.binlog_stmt_cache', 'line'], 'lines': [ - ["Binlog_stmt_cache_disk_use", "disk", "incremental"], - ["Binlog_stmt_cache_use", "all", "incremental"] + ['Binlog_stmt_cache_disk_use', 'disk', 'incremental'], + ['Binlog_stmt_cache_use', 'all', 'incremental'] ]}, 'connection_errors': { 'options': [None, 'mysql Connection Errors', 'connections/s', 'connections', 'mysql.connection_errors', 'line'], 'lines': [ - ["Connection_errors_accept", "accept", "incremental"], - ["Connection_errors_internal", "internal", "incremental"], - ["Connection_errors_max_connections", "max", "incremental"], - ["Connection_errors_peer_address", "peer_addr", "incremental"], - ["Connection_errors_select", "select", "incremental"], - ["Connection_errors_tcpwrap", "tcpwrap", "incremental"] + ['Connection_errors_accept', 'accept', 'incremental'], + ['Connection_errors_internal', 'internal', 'incremental'], + ['Connection_errors_max_connections', 'max', 'incremental'], + ['Connection_errors_peer_address', 'peer_addr', 'incremental'], + ['Connection_errors_select', 'select', 'incremental'], + ['Connection_errors_tcpwrap', 'tcpwrap', 'incremental'] ]}, 'slave_behind': { 'options': [None, 'Slave Behind Seconds', 'seconds', 'slave', 'mysql.slave_behind', 'line'], 'lines': [ - ["slave_behind", "seconds", "absolute"] + ['Seconds_Behind_Master', 'seconds', 'absolute'] ]}, 'slave_status': { 'options': [None, 'Slave Status', 'status', 'slave', 'mysql.slave_status', 'line'], 'lines': [ - ["slave_sql", "sql_running", "absolute"], - ["slave_io", "io_running", "absolute"] + ['Slave_SQL_Running', 'sql_running', 'absolute'], + ['Slave_IO_Running', 'io_running', 'absolute'] ]} } -class Service(SimpleService): +class Service(MySQLService): def __init__(self, configuration=None, name=None): - SimpleService.__init__(self, configuration=configuration, name=name) - self._parse_config(configuration) + MySQLService.__init__(self, configuration=configuration, name=name) self.order = ORDER self.definitions = CHARTS - self.connection = None - self.do_slave = -1 - - def _parse_config(self, configuration): - """ - Parse configuration to collect data from MySQL server - :param configuration: dict - :return: dict - """ - parameters = {} - if self.name is None: - self.name = 'local' - if 'user' in configuration: - parameters['user'] = self.configuration['user'] - if 'pass' in configuration: - parameters['passwd'] = self.configuration['pass'] - if 'my.cnf' in configuration: - parameters['read_default_file'] = self.configuration['my.cnf'] - elif 'socket' in configuration: - parameters['unix_socket'] = self.configuration['socket'] - elif 'host' in configuration: - parameters['host'] = self.configuration['host'] - if 'port' in configuration: - parameters['port'] = int(self.configuration['port']) - self.connection_parameters = parameters - - def _connect(self): - """ - Try to connect to MySQL server - """ - try: - self.connection = MySQLdb.connect(connect_timeout=self.update_every, **self.connection_parameters) - except MySQLdb.OperationalError as e: - self.error("Cannot establish connection to MySQL.") - self.debug(str(e)) - raise RuntimeError - except Exception as e: - self.error("problem connecting to server:", e) - raise RuntimeError - - def _get_data_slave(self): - """ - Get slave raw data from MySQL server - :return: dict - """ - if self.connection is None: - try: - self._connect() - except RuntimeError: - return None - - slave_data = None - slave_raw_data = None - try: - cursor = self.connection.cursor() - if cursor.execute(QUERY_SLAVE): - slave_raw_data = dict(list(zip([elem[0] for elem in cursor.description], cursor.fetchone()))) - - except MySQLdb.OperationalError as e: - self.debug("Reconnecting for query", QUERY_SLAVE, ":", str(e)) - try: - self._connect() - cursor = self.connection.cursor() - if cursor.execute(QUERY_SLAVE): - slave_raw_data = dict(list(zip([elem[0] for elem in cursor.description], cursor.fetchone()))) - except Exception as e: - self.error("retried, but cannot execute query", QUERY_SLAVE, ":", str(e)) - self.connection.close() - self.connection = None - return None - - except Exception as e: - self.error("cannot execute query", QUERY_SLAVE, ":", str(e)) - self.connection.close() - self.connection = None - return None - - if slave_raw_data is not None: - slave_data = { - 'slave_behind': None, - 'slave_sql': None, - 'slave_io': None - } - - try: - slave_data['slave_behind'] = int(slave_raw_data.setdefault('Seconds_Behind_Master', -1)) - except: - slave_data['slave_behind'] = None - - try: - slave_data['slave_sql'] = 1 if slave_raw_data.get('Slave_SQL_Running') == 'Yes' else -1 - except: - slave_data['slave_sql'] = None - - try: - slave_data['slave_io'] = 1 if slave_raw_data.get('Slave_IO_Running') == 'Yes' else -1 - except: - slave_data['slave_io'] = None - - return slave_data + self.queries = dict(global_status=QUERY_GLOBAL, slave_status=QUERY_SLAVE) def _get_data(self): - """ - Get raw data from MySQL server - :return: dict - """ - if self.connection is None: - try: - self._connect() - except RuntimeError: - return None - try: - cursor = self.connection.cursor() - cursor.execute(QUERY) - raw_data = cursor.fetchall() - except MySQLdb.OperationalError as e: - self.debug("Reconnecting for query", QUERY, ":", str(e)) - try: - self._connect() - cursor = self.connection.cursor() - cursor.execute(QUERY) - raw_data = cursor.fetchall() - except Exception as e: - self.error("retried, but cannot execute query", QUERY, ":", str(e)) - self.connection.close() - self.connection = None - return None + raw_data = self._get_raw_data(description=True) - except Exception as e: - self.error("cannot execute query", QUERY, ":", str(e)) - self.connection.close() - self.connection = None - return None + if not raw_data: + return None - data = dict(raw_data) + to_netdata = dict() - # check for slave data - # the first time is -1 (so we do it) - # then it is set to 1 or 0 and we keep it like that - if self.do_slave != 0: - slave_data = self._get_data_slave() - if slave_data is not None: - data.update(slave_data) - if self.do_slave == -1: - self.do_slave = 1 - else: - if self.do_slave == -1: - self.error("replication metrics will be disabled - please allow netdata to collect them.") - self.do_slave = 0 + if 'global_status' in raw_data: + global_status = dict(raw_data['global_status'][0]) + for key in GLOBAL_STATS: + if key in global_status: + to_netdata[key] = global_status[key] + if 'Threads_created' in to_netdata and 'Connections' in to_netdata: + to_netdata['Thread_cache_misses'] = round(int(to_netdata['Threads_created']) / float(to_netdata['Connections']) * 10000) - # do calculations - try: - data["Thread_cache_misses"] = round(float(data["Threads_created"]) / float(data["Connections"]) * 10000) - except: - data["Thread_cache_misses"] = None + if 'slave_status' in raw_data: + if raw_data['slave_status'][0]: + slave_raw_data = dict(zip([e[0] for e in raw_data['slave_status'][1]], raw_data['slave_status'][0][0])) + for key, function in SLAVE_STATS: + if key in slave_raw_data: + to_netdata[key] = function(slave_raw_data[key]) + else: + self.queries.pop('slave_status') - return data + return to_netdata or None - def check(self): - """ - Check if service is able to connect to server - :return: boolean - """ - try: - self.connection = self._connect() - return True - except RuntimeError: - self.connection = None - return False diff --git a/python.d/nginx_log.chart.py b/python.d/nginx_log.chart.py deleted file mode 100644 index ef964a565..000000000 --- a/python.d/nginx_log.chart.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- -# Description: nginx log netdata python.d module -# Author: Pawel Krupa (paulfantom) - -from base import LogService -import re - -priority = 60000 -retries = 60 -# update_every = 3 - -ORDER = ['codes'] -CHARTS = { - 'codes': { - 'options': [None, 'nginx status codes', 'requests/s', 'requests', 'nginx_log.codes', 'stacked'], - 'lines': [ - ["2xx", None, "incremental"], - ["5xx", None, "incremental"], - ["3xx", None, "incremental"], - ["4xx", None, "incremental"], - ["1xx", None, "incremental"], - ["other", None, "incremental"] - ]} -} - - -class Service(LogService): - def __init__(self, configuration=None, name=None): - LogService.__init__(self, configuration=configuration, name=name) - if len(self.log_path) == 0: - self.log_path = "/var/log/nginx/access.log" - self.order = ORDER - self.definitions = CHARTS - pattern = r'" ([0-9]{3}) ?' - #pattern = r'(?:" )([0-9][0-9][0-9]) ?' - self.regex = re.compile(pattern) - - self.data = { - '1xx': 0, - '2xx': 0, - '3xx': 0, - '4xx': 0, - '5xx': 0, - 'other': 0 - } - - def _get_data(self): - """ - Parse new log lines - :return: dict - """ - try: - raw = self._get_raw_data() - if raw is None: - return None - elif not raw: - return self.data - except (ValueError, AttributeError): - return None - - regex = self.regex - for line in raw: - code = regex.search(line) - try: - beginning = code.group(1)[0] - except AttributeError: - continue - - if beginning == '2': - self.data["2xx"] += 1 - elif beginning == '3': - self.data["3xx"] += 1 - elif beginning == '4': - self.data["4xx"] += 1 - elif beginning == '5': - self.data["5xx"] += 1 - elif beginning == '1': - self.data["1xx"] += 1 - else: - self.data["other"] += 1 - - return self.data diff --git a/python.d/nsd.chart.py b/python.d/nsd.chart.py new file mode 100644 index 000000000..68bb4f237 --- /dev/null +++ b/python.d/nsd.chart.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# Description: NSD `nsd-control stats_noreset` netdata python.d module +# Author: <383c57 at gmail.com> + + +from base import ExecutableService +import re + +# default module values (can be overridden per job in `config`) +priority = 60000 +retries = 5 +update_every = 30 + +# charts order (can be overridden if you want less charts, or different order) +ORDER = ['queries', 'zones', 'protocol', 'type', 'transfer', 'rcode'] + +CHARTS = { + 'queries': { + 'options': [ + None, "queries", 'queries/s', 'queries', 'nsd.queries', 'line'], + 'lines': [ + ['num_queries', 'queries', 'incremental'],]}, + 'zones': { + 'options': [ + None, "zones", 'zones', 'zones', 'nsd.zones', 'stacked'], + 'lines': [ + ['zone_master', 'master', 'absolute'], + ['zone_slave', 'slave', 'absolute'],]}, + 'protocol': { + 'options': [ + None, "protocol", 'queries/s', 'protocol', 'nsd.protocols', 'stacked'], + 'lines': [ + ['num_udp', 'udp', 'incremental'], + ['num_udp6', 'udp6', 'incremental'], + ['num_tcp', 'tcp', 'incremental'], + ['num_tcp6', 'tcp6', 'incremental'],]}, + 'type': { + 'options': [ + None, "query type", 'queries/s', 'query type', 'nsd.type', 'stacked'], + 'lines': [ + ['num_type_A', 'A', 'incremental'], + ['num_type_NS', 'NS', 'incremental'], + ['num_type_CNAME', 'CNAME', 'incremental'], + ['num_type_SOA', 'SOA', 'incremental'], + ['num_type_PTR', 'PTR', 'incremental'], + ['num_type_HINFO', 'HINFO', 'incremental'], + ['num_type_MX', 'MX', 'incremental'], + ['num_type_NAPTR', 'NAPTR', 'incremental'], + ['num_type_TXT', 'TXT', 'incremental'], + ['num_type_AAAA', 'AAAA', 'incremental'], + ['num_type_SRV', 'SRV', 'incremental'], + ['num_type_TYPE255', 'ANY', 'incremental'],]}, + 'transfer': { + 'options': [ + None, "transfer", 'queries/s', 'transfer', 'nsd.transfer', 'stacked'], + 'lines': [ + ['num_opcode_NOTIFY', 'NOTIFY', 'incremental'], + ['num_type_TYPE252', 'AXFR', 'incremental'],]}, + 'rcode': { + 'options': [ + None, "return code", 'queries/s', 'return code', 'nsd.rcode', 'stacked'], + 'lines': [ + ['num_rcode_NOERROR', 'NOERROR', 'incremental'], + ['num_rcode_FORMERR', 'FORMERR', 'incremental'], + ['num_rcode_SERVFAIL', 'SERVFAIL', 'incremental'], + ['num_rcode_NXDOMAIN', 'NXDOMAIN', 'incremental'], + ['num_rcode_NOTIMP', 'NOTIMP', 'incremental'], + ['num_rcode_REFUSED', 'REFUSED', 'incremental'], + ['num_rcode_YXDOMAIN', 'YXDOMAIN', 'incremental'],]} +} + + +class Service(ExecutableService): + def __init__(self, configuration=None, name=None): + ExecutableService.__init__( + self, configuration=configuration, name=name) + self.command = "nsd-control stats_noreset" + self.order = ORDER + self.definitions = CHARTS + self.regex = re.compile(r'([A-Za-z0-9.]+)=(\d+)') + + def _get_data(self): + lines = self._get_raw_data() + if not lines: + return None + + r = self.regex + stats = dict((k.replace('.', '_'), int(v)) + for k, v in r.findall(''.join(lines))) + stats.setdefault('num_opcode_NOTIFY', 0) + stats.setdefault('num_type_TYPE252', 0) + stats.setdefault('num_type_TYPE255', 0) + return stats diff --git a/python.d/phpfpm.chart.py b/python.d/phpfpm.chart.py old mode 100755 new mode 100644 index b79a35d75..7a9835210 --- a/python.d/phpfpm.chart.py +++ b/python.d/phpfpm.chart.py @@ -4,6 +4,7 @@ from base import UrlService import json +import re # default module values (can be overridden per job in `config`) # update_every = 2 @@ -19,44 +20,75 @@ retries = 60 # }} # charts order (can be overridden if you want less charts, or different order) + +POOL_INFO = [ + ('active processes', 'active'), + ('max active processes', 'maxActive'), + ('idle processes', 'idle'), + ('accepted conn', 'requests'), + ('max children reached', 'reached'), + ('slow requests', 'slow') +] + +PER_PROCESS_INFO = [ + ('request duration', 'ReqDur'), + ('last request cpu', 'ReqCpu'), + ('last request memory', 'ReqMem') +] + + +def average(collection): + return sum(collection, 0.0) / max(len(collection), 1) + +CALC = [ + ('min', min), + ('max', max), + ('avg', average) +] + ORDER = ['connections', 'requests', 'performance', 'request_duration', 'request_cpu', 'request_mem'] CHARTS = { 'connections': { - 'options': [None, 'PHP-FPM Active Connections', 'connections', 'active connections', 'phpfpm.connections', 'line'], + 'options': [None, 'PHP-FPM Active Connections', 'connections', 'active connections', 'phpfpm.connections', + 'line'], 'lines': [ - ["active"], - ["maxActive", 'max active'], - ["idle"] + ['active'], + ['maxActive', 'max active'], + ['idle'] ]}, 'requests': { 'options': [None, 'PHP-FPM Requests', 'requests/s', 'requests', 'phpfpm.requests', 'line'], 'lines': [ - ["requests", None, "incremental"] + ['requests', None, 'incremental'] ]}, 'performance': { 'options': [None, 'PHP-FPM Performance', 'status', 'performance', 'phpfpm.performance', 'line'], 'lines': [ - ["reached", 'max children reached'], - ["slow", 'slow requests'] + ['reached', 'max children reached'], + ['slow', 'slow requests'] ]}, 'request_duration': { - 'options': [None, 'PHP-FPM Request Duration', 'milliseconds', 'request duration', 'phpfpm.request_duration', 'line'], + 'options': [None, 'PHP-FPM Request Duration', 'milliseconds', 'request duration', 'phpfpm.request_duration', + 'line'], 'lines': [ - ["maxReqDur", 'max request duration'], - ["avgReqDur", 'average request duration'] + ['minReqDur', 'min', 'absolute', 1, 1000], + ['maxReqDur', 'max', 'absolute', 1, 1000], + ['avgReqDur', 'avg', 'absolute', 1, 1000] ]}, 'request_cpu': { 'options': [None, 'PHP-FPM Request CPU', 'percent', 'request CPU', 'phpfpm.request_cpu', 'line'], 'lines': [ - ["maxReqCPU", 'max request cpu'], - ["avgReqCPU", 'average request cpu'] + ['minReqCpu', 'min'], + ['maxReqCpu', 'max'], + ['avgReqCpu', 'avg'] ]}, 'request_mem': { 'options': [None, 'PHP-FPM Request Memory', 'kilobytes', 'request memory', 'phpfpm.request_mem', 'line'], 'lines': [ - ["maxReqMem", 'max request memory'], - ["avgReqMem", 'average request memory'] + ['minReqMem', 'min', 'absolute', 1, 1024], + ['maxReqMem', 'max', 'absolute', 1, 1024], + ['avgReqMem', 'avg', 'absolute', 1, 1024] ]} } @@ -64,76 +96,73 @@ CHARTS = { class Service(UrlService): def __init__(self, configuration=None, name=None): UrlService.__init__(self, configuration=configuration, name=name) - if len(self.url) == 0: - self.url = "http://localhost/status?full&json" + self.url = self.configuration.get('url', 'http://localhost/status?full&json') self.order = ORDER self.definitions = CHARTS - self.assignment = {"active processes": 'active', - "max active processes": 'maxActive', - "idle processes": 'idle', - "accepted conn": 'requests', - "max children reached": 'reached', - "slow requests": 'slow'} - self.proc_assignment = {"request duration": 'ReqDur', - "last request cpu": 'ReqCPU', - "last request memory": 'ReqMem'} + self.regex = re.compile(r'([a-z][a-z ]+): ([\d.]+)') + self.json = '&json' in self.url or '?json' in self.url + self.json_full = self.url.endswith(('?full&json', '?json&full')) + self.if_all_processes_running = dict([(c_name + p_name, 0) for c_name, func in CALC + for metric, p_name in PER_PROCESS_INFO]) def _get_data(self): """ Format data received from http request :return: dict """ - try: - raw = self._get_raw_data() - except AttributeError: + raw = self._get_raw_data() + if not raw: return None - if '?json' in self.url or '&json' in self.url: - try: - raw_json = json.loads(raw) - except ValueError: - return None - data = {} - for k,v in raw_json.items(): - if k in self.assignment: - data[self.assignment[k]] = v - - if '&full' in self.url or '?full' in self.url: - c = 0 - sum_val = {} - for proc in raw_json['processes']: - if proc['state'] != 'Idle': - continue - c += 1 - for k, v in self.proc_assignment.items(): - d = proc[k] - if v == 'ReqDur': - d = d/1000 - if v == 'ReqMem': - d = d/1024 - if 'max' + v not in data or data['max' + v] < d: - data['max' + v] = d - if 'avg' + v not in sum_val: - sum_val['avg' + v] = 0 - data['avg' + v] = 0 - sum_val['avg' + v] += d - if len(sum_val): - for k, v in sum_val.items(): - data[k] = v/c - - if len(data) == 0: - return None - return data - - raw = raw.split('\n') - data = {} - for row in raw: - tmp = row.split(":") - if str(tmp[0]) in self.assignment: - try: - data[self.assignment[tmp[0]]] = int(tmp[1]) - except (IndexError, ValueError): - pass - if len(data) == 0: - return None - return data + raw_json = parse_raw_data_(is_json=self.json, regex=self.regex, raw_data=raw) + + # Per Pool info: active connections, requests and performance charts + to_netdata = fetch_data_(raw_data=raw_json, metrics_list=POOL_INFO) + + # Per Process Info: duration, cpu and memory charts (min, max, avg) + if self.json_full: + p_info = dict() + to_netdata.update(self.if_all_processes_running) # If all processes are in running state + # Metrics are always 0 if the process is not in Idle state because calculation is done + # when the request processing has terminated + for process in [p for p in raw_json['processes'] if p['state'] == 'Idle']: + p_info.update(fetch_data_(raw_data=process, metrics_list=PER_PROCESS_INFO, pid=str(process['pid']))) + + if p_info: + for new_name in PER_PROCESS_INFO: + for name, function in CALC: + to_netdata[name + new_name[1]] = function([p_info[k] for k in p_info if new_name[1] in k]) + + return to_netdata or None + + +def fetch_data_(raw_data, metrics_list, pid=''): + """ + :param raw_data: dict + :param metrics_list: list + :param pid: str + :return: dict + """ + result = dict() + for metric, new_name in metrics_list: + if metric in raw_data: + result[new_name + pid] = float(raw_data[metric]) + return result + + +def parse_raw_data_(is_json, regex, raw_data): + """ + :param is_json: bool + :param regex: compiled regular expr + :param raw_data: dict + :return: dict + """ + if is_json: + try: + return json.loads(raw_data) + except ValueError: + return dict() + else: + raw_data = ' '.join(raw_data.split()) + return dict(regex.findall(raw_data)) + diff --git a/python.d/postgres.chart.py b/python.d/postgres.chart.py index 919b6f8ee..d359bb4f7 100644 --- a/python.d/postgres.chart.py +++ b/python.d/postgres.chart.py @@ -2,12 +2,16 @@ # Description: example netdata python.d module # Authors: facetoe, dangtranhoang -import re from copy import deepcopy -import psycopg2 -from psycopg2 import extensions -from psycopg2.extras import DictCursor +try: + import psycopg2 + from psycopg2 import extensions + from psycopg2.extras import DictCursor + from psycopg2 import OperationalError + PSYCOPG2 = True +except ImportError: + PSYCOPG2 = False from base import SimpleService @@ -16,39 +20,70 @@ update_every = 1 priority = 90000 retries = 60 -ARCHIVE = """ +METRICS = dict( + DATABASE=['connections', + 'xact_commit', + 'xact_rollback', + 'blks_read', + 'blks_hit', + 'tup_returned', + 'tup_fetched', + 'tup_inserted', + 'tup_updated', + 'tup_deleted', + 'conflicts', + 'size'], + BACKENDS=['backends_active', + 'backends_idle'], + INDEX_STATS=['index_count', + 'index_size'], + TABLE_STATS=['table_size', + 'table_count'], + ARCHIVE=['ready_count', + 'done_count', + 'file_count'], + BGWRITER=['writer_scheduled', + 'writer_requested'], + LOCKS=['ExclusiveLock', + 'RowShareLock', + 'SIReadLock', + 'ShareUpdateExclusiveLock', + 'AccessExclusiveLock', + 'AccessShareLock', + 'ShareRowExclusiveLock', + 'ShareLock', + 'RowExclusiveLock'] +) + +QUERIES = dict( + ARCHIVE=""" SELECT CAST(COUNT(*) AS INT) AS file_count, CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.ready$$r$ as INT)), 0) AS INT) AS ready_count, CAST(COALESCE(SUM(CAST(archive_file ~ $r$\.done$$r$ AS INT)), 0) AS INT) AS done_count FROM pg_catalog.pg_ls_dir('pg_xlog/archive_status') AS archive_files (archive_file); -""" - -BACKENDS = """ +""", + BACKENDS=""" SELECT count(*) - (SELECT count(*) FROM pg_stat_activity WHERE state = 'idle') AS backends_active, (SELECT count(*) FROM pg_stat_activity WHERE state = 'idle' ) AS backends_idle -FROM - pg_stat_activity; -""" - -TABLE_STATS = """ +FROM pg_stat_activity; +""", + TABLE_STATS=""" SELECT - ((sum(relpages) * 8) * 1024) AS size_relations, - count(1) AS relations + ((sum(relpages) * 8) * 1024) AS table_size, + count(1) AS table_count FROM pg_class WHERE relkind IN ('r', 't'); -""" - -INDEX_STATS = """ +""", + INDEX_STATS=""" SELECT - ((sum(relpages) * 8) * 1024) AS size_indexes, - count(1) AS indexes + ((sum(relpages) * 8) * 1024) AS index_size, + count(1) AS index_count FROM pg_class -WHERE relkind = 'i';""" - -DATABASE = """ +WHERE relkind = 'i';""", + DATABASE=""" SELECT datname AS database_name, sum(numbackends) AS connections, @@ -61,93 +96,108 @@ SELECT sum(tup_inserted) AS tup_inserted, sum(tup_updated) AS tup_updated, sum(tup_deleted) AS tup_deleted, - sum(conflicts) AS conflicts + sum(conflicts) AS conflicts, + pg_database_size(datname) AS size FROM pg_stat_database WHERE NOT datname ~* '^template\d+' GROUP BY database_name; -""" - -BGWRITER = 'SELECT * FROM pg_stat_bgwriter;' -DATABASE_LOCKS = """ +""", + BGWRITER=""" +SELECT + checkpoints_timed AS writer_scheduled, + checkpoints_req AS writer_requested +FROM pg_stat_bgwriter;""", + LOCKS=""" SELECT pg_database.datname as database_name, mode, - count(mode) AS count + count(mode) AS locks_count FROM pg_locks INNER JOIN pg_database ON pg_database.oid = pg_locks.database GROUP BY datname, mode ORDER BY datname, mode; -""" -REPLICATION = """ -SELECT - client_hostname, - client_addr, - state, - sent_offset - ( - replay_offset - (sent_xlog - replay_xlog) * 255 * 16 ^ 6 ) AS byte_lag -FROM ( - SELECT - client_addr, client_hostname, state, - ('x' || lpad(split_part(sent_location::text, '/', 1), 8, '0'))::bit(32)::bigint AS sent_xlog, - ('x' || lpad(split_part(replay_location::text, '/', 1), 8, '0'))::bit(32)::bigint AS replay_xlog, - ('x' || lpad(split_part(sent_location::text, '/', 2), 8, '0'))::bit(32)::bigint AS sent_offset, - ('x' || lpad(split_part(replay_location::text, '/', 2), 8, '0'))::bit(32)::bigint AS replay_offset - FROM pg_stat_replication -) AS s; -""" - -LOCK_TYPES = [ - 'ExclusiveLock', - 'RowShareLock', - 'SIReadLock', - 'ShareUpdateExclusiveLock', - 'AccessExclusiveLock', - 'AccessShareLock', - 'ShareRowExclusiveLock', - 'ShareLock', - 'RowExclusiveLock' -] - -ORDER = ['db_stat_transactions', 'db_stat_tuple_read', 'db_stat_tuple_returned', 'db_stat_tuple_write', +""", + FIND_DATABASES=""" +SELECT datname FROM pg_stat_database WHERE NOT datname ~* '^template\d+' +""", + IF_SUPERUSER=""" +SELECT current_setting('is_superuser') = 'on' AS is_superuser; + """) + +# REPLICATION = """ +# SELECT +# client_hostname, +# client_addr, +# state, +# sent_offset - ( +# replay_offset - (sent_xlog - replay_xlog) * 255 * 16 ^ 6 ) AS byte_lag +# FROM ( +# SELECT +# client_addr, client_hostname, state, +# ('x' || lpad(split_part(sent_location::text, '/', 1), 8, '0'))::bit(32)::bigint AS sent_xlog, +# ('x' || lpad(split_part(replay_location::text, '/', 1), 8, '0'))::bit(32)::bigint AS replay_xlog, +# ('x' || lpad(split_part(sent_location::text, '/', 2), 8, '0'))::bit(32)::bigint AS sent_offset, +# ('x' || lpad(split_part(replay_location::text, '/', 2), 8, '0'))::bit(32)::bigint AS replay_offset +# FROM pg_stat_replication +# ) AS s; +# """ + + +QUERY_STATS = { + QUERIES['DATABASE']: METRICS['DATABASE'], + QUERIES['BACKENDS']: METRICS['BACKENDS'], + QUERIES['ARCHIVE']: METRICS['ARCHIVE'], + QUERIES['LOCKS']: METRICS['LOCKS'] +} + +ORDER = ['db_stat_transactions', 'db_stat_tuple_read', 'db_stat_tuple_returned', 'db_stat_tuple_write', 'database_size', 'backend_process', 'index_count', 'index_size', 'table_count', 'table_size', 'wal', 'background_writer'] CHARTS = { 'db_stat_transactions': { - 'options': [None, 'Transactions on db', 'transactions/s', 'db statistics', 'postgres.db_stat_transactions', 'line'], + 'options': [None, 'Transactions on db', 'transactions/s', 'db statistics', 'postgres.db_stat_transactions', + 'line'], 'lines': [ - ['db_stat_xact_commit', 'committed', 'incremental'], - ['db_stat_xact_rollback', 'rolled back', 'incremental'] + ['xact_commit', 'committed', 'incremental'], + ['xact_rollback', 'rolled back', 'incremental'] ]}, 'db_stat_connections': { - 'options': [None, 'Current connections to db', 'count', 'db statistics', 'postgres.db_stat_connections', 'line'], + 'options': [None, 'Current connections to db', 'count', 'db statistics', 'postgres.db_stat_connections', + 'line'], 'lines': [ - ['db_stat_connections', 'connections', 'absolute'] + ['connections', 'connections', 'absolute'] ]}, 'db_stat_tuple_read': { 'options': [None, 'Tuple reads from db', 'reads/s', 'db statistics', 'postgres.db_stat_tuple_read', 'line'], 'lines': [ - ['db_stat_blks_read', 'disk', 'incremental'], - ['db_stat_blks_hit', 'cache', 'incremental'] + ['blks_read', 'disk', 'incremental'], + ['blks_hit', 'cache', 'incremental'] ]}, 'db_stat_tuple_returned': { - 'options': [None, 'Tuples returned from db', 'tuples/s', 'db statistics', 'postgres.db_stat_tuple_returned', 'line'], + 'options': [None, 'Tuples returned from db', 'tuples/s', 'db statistics', 'postgres.db_stat_tuple_returned', + 'line'], 'lines': [ - ['db_stat_tup_returned', 'sequential', 'incremental'], - ['db_stat_tup_fetched', 'bitmap', 'incremental'] + ['tup_returned', 'sequential', 'incremental'], + ['tup_fetched', 'bitmap', 'incremental'] ]}, 'db_stat_tuple_write': { 'options': [None, 'Tuples written to db', 'writes/s', 'db statistics', 'postgres.db_stat_tuple_write', 'line'], 'lines': [ - ['db_stat_tup_inserted', 'inserted', 'incremental'], - ['db_stat_tup_updated', 'updated', 'incremental'], - ['db_stat_tup_deleted', 'deleted', 'incremental'], - ['db_stat_conflicts', 'conflicts', 'incremental'] + ['tup_inserted', 'inserted', 'incremental'], + ['tup_updated', 'updated', 'incremental'], + ['tup_deleted', 'deleted', 'incremental'], + ['conflicts', 'conflicts', 'incremental'] + ]}, + 'database_size': { + 'options': [None, 'Database size', 'MB', 'database size', 'postgres.db_size', 'stacked'], + 'lines': [ ]}, 'backend_process': { - 'options': [None, 'Current Backend Processes', 'processes', 'backend processes', 'postgres.backend_process', 'line'], + 'options': [None, 'Current Backend Processes', 'processes', 'backend processes', 'postgres.backend_process', + 'line'], 'lines': [ - ['backend_process_active', 'active', 'absolute'], - ['backend_process_idle', 'idle', 'absolute'] + ['backends_active', 'active', 'absolute'], + ['backends_idle', 'idle', 'absolute'] ]}, 'index_count': { 'options': [None, 'Total indexes', 'index', 'indexes', 'postgres.index_count', 'line'], @@ -172,15 +222,15 @@ CHARTS = { 'wal': { 'options': [None, 'Write-Ahead Logging Statistics', 'files/s', 'write ahead log', 'postgres.wal', 'line'], 'lines': [ - ['wal_total', 'total', 'incremental'], - ['wal_ready', 'ready', 'incremental'], - ['wal_done', 'done', 'incremental'] + ['file_count', 'total', 'incremental'], + ['ready_count', 'ready', 'incremental'], + ['done_count', 'done', 'incremental'] ]}, 'background_writer': { 'options': [None, 'Checkpoints', 'writes/s', 'background writer', 'postgres.background_writer', 'line'], 'lines': [ - ['background_writer_scheduled', 'scheduled', 'incremental'], - ['background_writer_requested', 'requested', 'incremental'] + ['writer_scheduled', 'scheduled', 'incremental'], + ['writer_requested', 'requested', 'incremental'] ]} } @@ -188,199 +238,169 @@ CHARTS = { class Service(SimpleService): def __init__(self, configuration=None, name=None): super(self.__class__, self).__init__(configuration=configuration, name=name) - self.order = ORDER - self.definitions = CHARTS - self.table_stats = configuration.pop('table_stats', True) - self.index_stats = configuration.pop('index_stats', True) + self.order = ORDER[:] + self.definitions = deepcopy(CHARTS) + self.table_stats = configuration.pop('table_stats', False) + self.index_stats = configuration.pop('index_stats', False) self.configuration = configuration - self.connection = None + self.connection = False self.is_superuser = False - self.data = {} - self.databases = set() + self.data = dict() + self.locks_zeroed = dict() + self.databases = list() def _connect(self): params = dict(user='postgres', database=None, password=None, - host='localhost', + host=None, port=5432) params.update(self.configuration) if not self.connection: - self.connection = psycopg2.connect(**params) - self.connection.set_isolation_level(extensions.ISOLATION_LEVEL_AUTOCOMMIT) - self.connection.set_session(readonly=True) + try: + self.connection = psycopg2.connect(**params) + self.connection.set_isolation_level(extensions.ISOLATION_LEVEL_AUTOCOMMIT) + self.connection.set_session(readonly=True) + except OperationalError as error: + return False, str(error) + return True, True def check(self): + if not PSYCOPG2: + self.error('\'python-psycopg2\' module is needed to use postgres.chart.py') + return False + result, error = self._connect() + if not result: + conf = dict([(k, (lambda k, v: v if k != 'password' else '*****')(k, v)) for k, v in self.configuration.items()]) + self.error('Failed to connect to %s. Error: %s' % (str(conf), error)) + return False try: - self._connect() cursor = self.connection.cursor() - self._discover_databases(cursor) - self._check_if_superuser(cursor) + self.databases = discover_databases_(cursor, QUERIES['FIND_DATABASES']) + is_superuser = check_if_superuser_(cursor, QUERIES['IF_SUPERUSER']) cursor.close() - self._create_definitions() + self.locks_zeroed = populate_lock_types(self.databases) + self.add_additional_queries_(is_superuser) + self.create_dynamic_charts_() return True - except Exception as e: - self.error(str(e)) + except Exception as error: + self.error(str(error)) return False - def _discover_databases(self, cursor): - cursor.execute(""" - SELECT datname - FROM pg_stat_database - WHERE NOT datname ~* '^template\d+' - """) - self.databases = set(r[0] for r in cursor) - - def _check_if_superuser(self, cursor): - cursor.execute(""" - SELECT current_setting('is_superuser') = 'on' AS is_superuser; - """) - self.is_superuser = cursor.fetchone()[0] - - def _create_definitions(self): - for database_name in self.databases: - for chart_template_name in list(CHARTS): - if chart_template_name.startswith('db_stat'): - self._add_database_stat_chart(chart_template_name, database_name) - self._add_database_lock_chart(database_name) - - def _add_database_stat_chart(self, chart_template_name, database_name): - chart_template = CHARTS[chart_template_name] - chart_name = "{0}_{1}".format(database_name, chart_template_name) - if chart_name not in self.order: - self.order.insert(0, chart_name) - name, title, units, family, context, chart_type = chart_template['options'] - self.definitions[chart_name] = { - 'options': [ - name, - title + ': ' + database_name, - units, - 'db ' + database_name, - context, - chart_type - ] - } - - self.definitions[chart_name]['lines'] = [] - for line in deepcopy(chart_template['lines']): - line[0] = "{0}_{1}".format(database_name, line[0]) - self.definitions[chart_name]['lines'].append(line) - - def _add_database_lock_chart(self, database_name): - chart_name = "{0}_locks".format(database_name) - if chart_name not in self.order: - self.order.insert(-1, chart_name) - self.definitions[chart_name] = dict( - options= - [ - None, - 'Locks on db: ' + database_name, - 'locks', - 'db ' + database_name, - 'postgres.db_locks', - 'line' - ], - lines=[] - ) - - for lock_type in LOCK_TYPES: - lock_id = "{0}_{1}".format(database_name, lock_type) - label = re.sub("([a-z])([A-Z])", "\g<1> \g<2>", lock_type) - self.definitions[chart_name]['lines'].append([lock_id, label, 'absolute']) - - def _get_data(self): - self._connect() - - cursor = self.connection.cursor(cursor_factory=DictCursor) - self.add_stats(cursor) - - cursor.close() - return self.data - - def add_stats(self, cursor): - self.add_database_stats(cursor) - self.add_backend_stats(cursor) + def add_additional_queries_(self, is_superuser): if self.index_stats: - self.add_index_stats(cursor) + QUERY_STATS[QUERIES['INDEX_STATS']] = METRICS['INDEX_STATS'] if self.table_stats: - self.add_table_stats(cursor) - self.add_lock_stats(cursor) - self.add_bgwriter_stats(cursor) + QUERY_STATS[QUERIES['TABLE_STATS']] = METRICS['TABLE_STATS'] + if is_superuser: + QUERY_STATS[QUERIES['BGWRITER']] = METRICS['BGWRITER'] - # self.add_replication_stats(cursor) + def create_dynamic_charts_(self): - if self.is_superuser: - self.add_wal_stats(cursor) + for database_name in self.databases[::-1]: + self.definitions['database_size']['lines'].append([database_name + '_size', + database_name, 'absolute', 1, 1024 * 1024]) + for chart_name in [name for name in CHARTS if name.startswith('db_stat')]: + add_database_stat_chart_(order=self.order, definitions=self.definitions, + name=chart_name, database_name=database_name) - def add_database_stats(self, cursor): - cursor.execute(DATABASE) - for row in cursor: - database_name = row.get('database_name') - self.data["{0}_{1}".format(database_name, 'db_stat_xact_commit')] = int(row.get('xact_commit', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_xact_rollback')] = int(row.get('xact_rollback', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_blks_read')] = int(row.get('blks_read', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_blks_hit')] = int(row.get('blks_hit', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_tup_returned')] = int(row.get('tup_returned', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_tup_fetched')] = int(row.get('tup_fetched', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_tup_inserted')] = int(row.get('tup_inserted', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_tup_updated')] = int(row.get('tup_updated', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_tup_deleted')] = int(row.get('tup_deleted', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_conflicts')] = int(row.get('conflicts', 0)) - self.data["{0}_{1}".format(database_name, 'db_stat_connections')] = int(row.get('connections', 0)) - - def add_backend_stats(self, cursor): - cursor.execute(BACKENDS) - temp = cursor.fetchone() - - self.data['backend_process_active'] = int(temp.get('backends_active', 0)) - self.data['backend_process_idle'] = int(temp.get('backends_idle', 0)) - - def add_index_stats(self, cursor): - cursor.execute(INDEX_STATS) - temp = cursor.fetchone() - self.data['index_count'] = int(temp.get('indexes', 0)) - self.data['index_size'] = int(temp.get('size_indexes', 0)) - - def add_table_stats(self, cursor): - cursor.execute(TABLE_STATS) - temp = cursor.fetchone() - self.data['table_count'] = int(temp.get('relations', 0)) - self.data['table_size'] = int(temp.get('size_relations', 0)) - - def add_lock_stats(self, cursor): - cursor.execute(DATABASE_LOCKS) - - # zero out all current lock values - for database_name in self.databases: - for lock_type in LOCK_TYPES: - self.data["{0}_{1}".format(database_name, lock_type)] = 0 - - # populate those that have current locks + add_database_lock_chart_(order=self.order, definitions=self.definitions, database_name=database_name) + + def _get_data(self): + result, error = self._connect() + if result: + cursor = self.connection.cursor(cursor_factory=DictCursor) + try: + self.data.update(self.locks_zeroed) + for query, metrics in QUERY_STATS.items(): + self.query_stats_(cursor, query, metrics) + + except OperationalError: + self.connection = False + cursor.close() + return None + else: + cursor.close() + return self.data + else: + return None + + def query_stats_(self, cursor, query, metrics): + cursor.execute(query) for row in cursor: - database_name, lock_type, lock_count = row - self.data["{0}_{1}".format(database_name, lock_type)] = lock_count - - def add_wal_stats(self, cursor): - cursor.execute(ARCHIVE) - temp = cursor.fetchone() - self.data['wal_total'] = int(temp.get('file_count', 0)) - self.data['wal_ready'] = int(temp.get('ready_count', 0)) - self.data['wal_done'] = int(temp.get('done_count', 0)) - - def add_bgwriter_stats(self, cursor): - cursor.execute(BGWRITER) - temp = cursor.fetchone() - self.data['background_writer_scheduled'] = temp.get('checkpoints_timed', 0) - self.data['background_writer_requested'] = temp.get('checkpoints_requests', 0) - -''' - def add_replication_stats(self, cursor): - cursor.execute(REPLICATION) - temp = cursor.fetchall() - for row in temp: - self.add_gauge_value('Replication/%s' % row.get('client_addr', 'Unknown'), - 'byte_lag', - int(row.get('byte_lag', 0))) -''' + for metric in metrics: + dimension_id = '_'.join([row['database_name'], metric]) if 'database_name' in row else metric + if metric in row: + self.data[dimension_id] = int(row[metric]) + elif 'locks_count' in row: + self.data[dimension_id] = row['locks_count'] if metric == row['mode'] else 0 + + +def discover_databases_(cursor, query): + cursor.execute(query) + result = list() + for db in [database[0] for database in cursor]: + if db not in result: + result.append(db) + return result + + +def check_if_superuser_(cursor, query): + cursor.execute(query) + return cursor.fetchone()[0] + + +def populate_lock_types(databases): + result = dict() + for database in databases: + for lock_type in METRICS['LOCKS']: + key = '_'.join([database, lock_type]) + result[key] = 0 + + return result + + +def add_database_lock_chart_(order, definitions, database_name): + def create_lines(database): + result = list() + for lock_type in METRICS['LOCKS']: + dimension_id = '_'.join([database, lock_type]) + result.append([dimension_id, lock_type, 'absolute']) + return result + + chart_name = database_name + '_locks' + order.insert(-1, chart_name) + definitions[chart_name] = { + 'options': + [None, 'Locks on db: ' + database_name, 'locks', 'db ' + database_name, 'postgres.db_locks', 'line'], + 'lines': create_lines(database_name) + } + + +def add_database_stat_chart_(order, definitions, name, database_name): + def create_lines(database, lines): + result = list() + for line in lines: + new_line = ['_'.join([database, line[0]])] + line[1:] + result.append(new_line) + return result + + chart_template = CHARTS[name] + chart_name = '_'.join([database_name, name]) + order.insert(0, chart_name) + name, title, units, family, context, chart_type = chart_template['options'] + definitions[chart_name] = { + 'options': [name, title + ': ' + database_name, units, 'db ' + database_name, context, chart_type], + 'lines': create_lines(database_name, chart_template['lines'])} + + +# +# def add_replication_stats(self, cursor): +# cursor.execute(REPLICATION) +# temp = cursor.fetchall() +# for row in temp: +# self.add_gauge_value('Replication/%s' % row.get('client_addr', 'Unknown'), +# 'byte_lag', +# int(row.get('byte_lag', 0))) diff --git a/python.d/python_modules/__init__.py b/python.d/python_modules/__init__.py old mode 100755 new mode 100644 diff --git a/python.d/python_modules/base.py b/python.d/python_modules/base.py index 320c54bae..859300eca 100644 --- a/python.d/python_modules/base.py +++ b/python.d/python_modules/base.py @@ -18,19 +18,39 @@ # using ".encode()" in one thread can block other threads as well (only in python2) import time -# import sys import os import socket import select +import threading +import msg +import ssl +from subprocess import Popen, PIPE +from sys import exc_info + +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse + try: import urllib.request as urllib2 except ImportError: import urllib2 -from subprocess import Popen, PIPE +try: + import MySQLdb + PYMYSQL = True +except ImportError: + try: + import pymysql as MySQLdb + PYMYSQL = True + except ImportError: + PYMYSQL = False -import threading -import msg +try: + PATH = os.getenv('PATH').split(':') +except AttributeError: + PATH = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'.split(':') # class BaseService(threading.Thread): @@ -61,6 +81,7 @@ class SimpleService(threading.Thread): self.__first_run = True self.order = [] self.definitions = {} + self._data_from_check = dict() if configuration is None: self.error("BaseService: no configuration parameters supplied. Cannot create Service.") raise RuntimeError @@ -385,7 +406,7 @@ class SimpleService(threading.Thread): Create charts :return: boolean """ - data = self._get_data() + data = self._data_from_check or self._get_data() if data is None: self.debug("failed to receive data during create().") return False @@ -431,101 +452,93 @@ class SimpleService(threading.Thread): return updated + @staticmethod + def find_binary(binary): + try: + if isinstance(binary, str): + binary = os.path.basename(binary) + return next(('/'.join([p, binary]) for p in PATH + if os.path.isfile('/'.join([p, binary])) + and os.access('/'.join([p, binary]), os.X_OK))) + else: + return None + except StopIteration: + return None + class UrlService(SimpleService): - # TODO add support for https connections def __init__(self, configuration=None, name=None): - self.url = "" - self.user = None - self.password = None - self.proxies = {} SimpleService.__init__(self, configuration=configuration, name=name) + self.url = self.configuration.get('url') + self.user = self.configuration.get('user') + self.password = self.configuration.get('pass') + self.ss_cert = self.configuration.get('ss_cert') def __add_openers(self): - # TODO add error handling - self.opener = urllib2.build_opener() - - # Proxy handling - # TODO currently self.proxies isn't parsed from configuration file - # if len(self.proxies) > 0: - # for proxy in self.proxies: - # url = proxy['url'] - # # TODO test this: - # if "user" in proxy and "pass" in proxy: - # if url.lower().startswith('https://'): - # url = 'https://' + proxy['user'] + ':' + proxy['pass'] + '@' + url[8:] - # else: - # url = 'http://' + proxy['user'] + ':' + proxy['pass'] + '@' + url[7:] - # # FIXME move proxy auth to sth like this: - # # passman = urllib2.HTTPPasswordMgrWithDefaultRealm() - # # passman.add_password(None, url, proxy['user'], proxy['password']) - # # opener.add_handler(urllib2.HTTPBasicAuthHandler(passman)) - # - # if url.lower().startswith('https://'): - # opener.add_handler(urllib2.ProxyHandler({'https': url})) - # else: - # opener.add_handler(urllib2.ProxyHandler({'https': url})) + def self_signed_cert(ss_cert): + if ss_cert: + try: + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + return urllib2.build_opener(urllib2.HTTPSHandler(context=ctx)) + except AttributeError: + return None + else: + return None + + self.opener = self_signed_cert(self.ss_cert) or urllib2.build_opener() # HTTP Basic Auth - if self.user is not None and self.password is not None: + if self.user and self.password: + url_parse = urlparse(self.url) + top_level_url = '://'.join([url_parse.scheme, url_parse.netloc]) passman = urllib2.HTTPPasswordMgrWithDefaultRealm() - passman.add_password(None, self.url, self.user, self.password) + passman.add_password(None, top_level_url, self.user, self.password) self.opener.add_handler(urllib2.HTTPBasicAuthHandler(passman)) self.debug("Enabling HTTP basic auth") - #urllib2.install_opener(opener) - - def _get_raw_data(self): + def _get_raw_data(self, custom_url=None): """ Get raw data from http request :return: str """ - raw = None + raw_data = None + f = None try: - f = self.opener.open(self.url, timeout=self.update_every * 2) - # f = urllib2.urlopen(self.url, timeout=self.update_every * 2) - except Exception as e: - self.error(str(e)) + f = self.opener.open(custom_url or self.url, timeout=self.update_every * 2) + raw_data = f.read().decode('utf-8', 'ignore') + except Exception as error: + self.error('Url: %s. Error: %s' %(custom_url or self.url, str(error))) return None - - try: - raw = f.read().decode('utf-8', 'ignore') - except Exception as e: - self.error(str(e)) finally: - f.close() - return raw + if f is not None: f.close() + + return raw_data or None def check(self): """ Format configuration data and try to connect to server :return: boolean """ - if self.name is None or self.name == str(None): - self.name = 'local' - self.chart_name += "_" + self.name - else: - self.name = str(self.name) - try: - self.url = str(self.configuration['url']) - except (KeyError, TypeError): - pass - try: - self.user = str(self.configuration['user']) - except (KeyError, TypeError): - pass - try: - self.password = str(self.configuration['pass']) - except (KeyError, TypeError): - pass + if not (self.url and isinstance(self.url, str)): + self.error('URL is not defined or type is not ') + return False self.__add_openers() - test = self._get_data() - if test is None or len(test) == 0: + try: + data = self._get_data() + except Exception as error: + self.error('_get_data() failed. Url: %s. Error: %s' % (self.url, error)) return False - else: + + if isinstance(data, dict) and data: + self._data_from_check = data return True + else: + self.error("_get_data() returned no data or type is not ") + return False class SocketService(SimpleService): @@ -829,63 +842,212 @@ class LogService(SimpleService): class ExecutableService(SimpleService): - bad_substrings = ('&', '|', ';', '>', '<') def __init__(self, configuration=None, name=None): - self.command = "" SimpleService.__init__(self, configuration=configuration, name=name) + self.command = None def _get_raw_data(self): """ Get raw data from executed command - :return: str + :return: """ try: p = Popen(self.command, stdout=PIPE, stderr=PIPE) - except Exception as e: - self.error("Executing command", self.command, "resulted in error:", str(e)) + except Exception as error: + self.error("Executing command", self.command, "resulted in error:", str(error)) return None - data = [] + data = list() for line in p.stdout.readlines(): - data.append(str(line.decode())) + data.append(line.decode()) - if len(data) == 0: - self.error("No data collected.") - return None - - return data + return data or None def check(self): """ Parse basic configuration, check if command is whitelisted and is returning values - :return: boolean + :return: """ - if self.name is not None or self.name != str(None): - self.name = "" + # Preference: 1. "command" from configuration file 2. "command" from plugin (if specified) + if 'command' in self.configuration: + self.command = self.configuration['command'] + + # "command" must be: 1.not None 2. type + if not (self.command and isinstance(self.command, str)): + self.error('Command is not defined or command type is not ') + return False + + # Split "command" into: 1. command 2. options + command, opts = self.command.split()[0], self.command.split()[1:] + + # Check for "bad" symbols in options. No pipes, redirects etc. TODO: what is missing? + bad_opts = set(''.join(opts)) & set(['&', '|', ';', '>', '<']) + if bad_opts: + self.error("Bad command argument(s): %s" % bad_opts) + return False + + # Find absolute path ('echo' => '/bin/echo') + if '/' not in command: + command = self.find_binary(command) + if not command: + self.error('Can\'t locate "%s" binary in PATH(%s)' % (self.command, PATH)) + return False + # Check if binary exist and executable else: - self.name = str(self.name) + if not (os.path.isfile(command) and os.access(command, os.X_OK)): + self.error('"%s" is not a file or not executable' % command) + return False + + self.command = [command] + opts if opts else [command] + try: - self.command = str(self.configuration['command']) - except (KeyError, TypeError): - self.info("No command specified. Using: '" + self.command + "'") - # Splitting self.command on every space so subprocess.Popen reads it properly - self.command = self.command.split(' ') + data = self._get_data() + except Exception as error: + self.error('_get_data() failed. Command: %s. Error: %s' % (self.command, error)) + return False - for arg in self.command[1:]: - if any(st in arg for st in self.bad_substrings): - self.error("Bad command argument:" + " ".join(self.command[1:])) - return False + if isinstance(data, dict) and data: + # We need this for create() method. No reason to execute get_data() again if result is not empty dict() + self._data_from_check = data + return True + else: + self.error("Command", str(self.command), "returned no data") + return False + + +class MySQLService(SimpleService): + + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.__connection = None + self.__conn_properties = dict() + self.extra_conn_properties = dict() + self.__queries = self.configuration.get('queries', dict()) + self.queries = dict() + + def __connect(self): + try: + connection = MySQLdb.connect(connect_timeout=self.update_every, **self.__conn_properties) + except (MySQLdb.MySQLError, TypeError, AttributeError) as error: + return None, str(error) + else: + return connection, None - # test command and search for it in /usr/sbin or /sbin when failed - base = self.command[0].split('/')[-1] - if self._get_raw_data() is None: - for prefix in ['/sbin/', '/usr/sbin/']: - self.command[0] = prefix + base - if os.path.isfile(self.command[0]): - break + def check(self): + def get_connection_properties(conf, extra_conf): + properties = dict() + if 'user' in conf and conf['user']: + properties['user'] = conf['user'] + if 'pass' in conf and conf['pass']: + properties['passwd'] = conf['pass'] + if 'socket' in conf and conf['socket']: + properties['unix_socket'] = conf['socket'] + elif 'host' in conf and conf['host']: + properties['host'] = conf['host'] + properties['port'] = int(conf.get('port', 3306)) + elif 'my.cnf' in conf and conf['my.cnf']: + properties['read_default_file'] = conf['my.cnf'] + if isinstance(extra_conf, dict) and extra_conf: + properties.update(extra_conf) + + return properties or None + + def is_valid_queries_dict(raw_queries, log_error): + """ + :param raw_queries: dict: + :param log_error: function: + :return: dict or None + + raw_queries is valid when: type and not empty after is_valid_query(for all queries) + """ + def is_valid_query(query): + return all([isinstance(query, str), + query.startswith(('SELECT', 'select', 'SHOW', 'show'))]) + + if hasattr(raw_queries, 'keys') and raw_queries: + valid_queries = dict([(n, q) for n, q in raw_queries.items() if is_valid_query(q)]) + bad_queries = set(raw_queries) - set(valid_queries) + + if bad_queries: + log_error('Removed query(s): %s' % bad_queries) + return valid_queries + else: + log_error('Unsupported "queries" format. Must be not empty ') + return None - if self._get_data() is None or len(self._get_data()) == 0: - self.error("Command", self.command, "returned no data") + if not PYMYSQL: + self.error('MySQLdb or PyMySQL module is needed to use mysql.chart.py plugin') return False - return True + # Preference: 1. "queries" from the configuration file 2. "queries" from the module + self.queries = self.__queries or self.queries + # Check if "self.queries" exist, not empty and all queries are in valid format + self.queries = is_valid_queries_dict(self.queries, self.error) + if not self.queries: + return None + + # Get connection properties + self.__conn_properties = get_connection_properties(self.configuration, self.extra_conn_properties) + if not self.__conn_properties: + self.error('Connection properties are missing') + return False + + # Create connection to the database + self.__connection, error = self.__connect() + if error: + self.error('Can\'t establish connection to MySQL: %s' % error) + return False + + try: + data = self._get_data() + except Exception as error: + self.error('_get_data() failed. Error: %s' % error) + return False + + if isinstance(data, dict) and data: + # We need this for create() method + self._data_from_check = data + return True + else: + self.error("_get_data() returned no data or type is not ") + return False + + def _get_raw_data(self, description=None): + """ + Get raw data from MySQL server + :return: dict: fetchall() or (fetchall(), description) + """ + + if not self.__connection: + self.__connection, error = self.__connect() + if error: + return None + + raw_data = dict() + queries = dict(self.queries) + try: + with self.__connection as cursor: + for name, query in queries.items(): + try: + cursor.execute(query) + except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as error: + if self.__is_error_critical(err_class=exc_info()[0], err_text=str(error)): + raise RuntimeError + self.error('Removed query: %s[%s]. Error: %s' + % (name, query, error)) + self.queries.pop(name) + continue + else: + raw_data[name] = (cursor.fetchall(), cursor.description) if description else cursor.fetchall() + self.__connection.commit() + except (MySQLdb.MySQLError, RuntimeError, TypeError, AttributeError): + self.__connection.close() + self.__connection = None + return None + else: + return raw_data or None + + @staticmethod + def __is_error_critical(err_class, err_text): + return err_class == MySQLdb.OperationalError and all(['denied' not in err_text, + 'Unknown column' not in err_text]) diff --git a/python.d/smartd_log.chart.py b/python.d/smartd_log.chart.py new file mode 100644 index 000000000..e80372379 --- /dev/null +++ b/python.d/smartd_log.chart.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- +# Description: smart netdata python.d module +# Author: l2isbad, vorph1 + +from re import compile +from os import listdir, access, R_OK +from os.path import isfile, join, getsize, basename, isdir +try: + from queue import Queue +except ImportError: + from Queue import Queue +from threading import Thread +from base import SimpleService +from collections import namedtuple + +# default module values (can be overridden per job in `config`) +update_every = 5 +priority = 60000 + +# charts order (can be overridden if you want less charts, or different order) +ORDER = ['1', '4', '5', '7', '9', '12', '193', '194', '197', '198', '200'] + +SMART_ATTR = { + '1': 'Read Error Rate', + '2': 'Throughput Performance', + '3': 'Spin-Up Time', + '4': 'Start/Stop Count', + '5': 'Reallocated Sectors Count', + '6': 'Read Channel Margin', + '7': 'Seek Error Rate', + '8': 'Seek Time Performance', + '9': 'Power-On Hours Count', + '10': 'Spin-up Retries', + '11': 'Calibration Retries', + '12': 'Power Cycle Count', + '13': 'Soft Read Error Rate', + '100': 'Erase/Program Cycles', + '103': 'Translation Table Rebuild', + '108': 'Unknown (108)', + '170': 'Reserved Block Count', + '171': 'Program Fail Count', + '172': 'Erase Fail Count', + '173': 'Wear Leveller Worst Case Erase Count', + '174': 'Unexpected Power Loss', + '175': 'Program Fail Count', + '176': 'Erase Fail Count', + '177': 'Wear Leveling Count', + '178': 'Used Reserved Block Count', + '179': 'Used Reserved Block Count', + '180': 'Unused Reserved Block Count', + '181': 'Program Fail Count', + '182': 'Erase Fail Count', + '183': 'SATA Downshifts', + '184': 'End-to-End error', + '185': 'Head Stability', + '186': 'Induced Op-Vibration Detection', + '187': 'Reported Uncorrectable Errors', + '188': 'Command Timeout', + '189': 'High Fly Writes', + '190': 'Temperature', + '191': 'G-Sense Errors', + '192': 'Power-Off Retract Cycles', + '193': 'Load/Unload Cycles', + '194': 'Temperature', + '195': 'Hardware ECC Recovered', + '196': 'Reallocation Events', + '197': 'Current Pending Sectors', + '198': 'Off-line Uncorrectable', + '199': 'UDMA CRC Error Rate', + '200': 'Write Error Rate', + '201': 'Soft Read Errors', + '202': 'Data Address Mark Errors', + '203': 'Run Out Cancel', + '204': 'Soft ECC Corrections', + '205': 'Thermal Asperity Rate', + '206': 'Flying Height', + '207': 'Spin High Current', + '209': 'Offline Seek Performance', + '220': 'Disk Shift', + '221': 'G-Sense Error Rate', + '222': 'Loaded Hours', + '223': 'Load/Unload Retries', + '224': 'Load Friction', + '225': 'Load/Unload Cycles', + '226': 'Load-in Time', + '227': 'Torque Amplification Count', + '228': 'Power-Off Retracts', + '230': 'GMR Head Amplitude', + '231': 'Temperature', + '232': 'Available Reserved Space', + '233': 'Media Wearout Indicator', + '240': 'Head Flying Hours', + '241': 'Total LBAs Written', + '242': 'Total LBAs Read', + '250': 'Read Error Retry Rate' +} + +NAMED_DISKS = namedtuple('disks', ['name', 'size', 'number']) + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.regex = compile(r'(\d+);(\d+);(\d+)') + self.log_path = self.configuration.get('log_path', '/var/log/smartd') + self.raw_values = self.configuration.get('raw_values') + self.attr = self.configuration.get('smart_attributes', []) + self.previous_data = dict() + + def check(self): + # Can\'t start without smartd readable diks log files + disks = find_disks_in_log_path(self.log_path) + if not disks: + self.error('Can\'t locate any smartd log files in %s' % self.log_path) + return False + + # List of namedtuples to track smartd log file size + self.disks = [NAMED_DISKS(name=disks[i], size=0, number=i) for i in range(len(disks))] + + if self._get_data(): + self.create_charts() + return True + else: + self.error('Can\'t collect any data. Sorry.') + return False + + def _get_raw_data(self, queue, disk): + # The idea is to open a file. + # Jump to the end. + # Seek backward until '\n' symbol appears + # If '\n' is found or it's the beginning of the file + # readline()! (last or first line) + with open(disk, 'rb') as f: + f.seek(-2, 2) + while f.read(1) != b'\n': + f.seek(-2, 1) + if f.tell() == 0: + break + result = f.readline() + + result = result.decode() + result = self.regex.findall(result) + + queue.put([basename(disk), result]) + + def _get_data(self): + threads, result = list(), list() + queue = Queue() + to_netdata = dict() + + # If the size has not changed there is no reason to poll log files. + disks = [disk for disk in self.disks if self.size_changed(disk)] + if disks: + for disk in disks: + th = Thread(target=self._get_raw_data, args=(queue, disk.name)) + th.start() + threads.append(th) + + for thread in threads: + thread.join() + result.append(queue.get()) + else: + # Data from last real poll + return self.previous_data or None + + for elem in result: + for a, n, r in elem[1]: + to_netdata.update({'_'.join([elem[0], a]): r if self.raw_values else n}) + + self.previous_data.update(to_netdata) + + return to_netdata or None + + def size_changed(self, disk): + # We are not interested in log files: + # 1. zero size + # 2. size is not changed since last poll + try: + size = getsize(disk.name) + if size != disk.size and size: + self.disks[disk.number] = disk._replace(size=size) + return True + else: + return False + except OSError: + # Remove unreadable/nonexisting log files from list of disks and previous_data + self.disks.remove(disk) + self.previous_data = dict([(k, v) for k, v in self.previous_data.items() if basename(disk.name) not in k]) + return False + + def create_charts(self): + + def create_lines(attrid): + result = list() + for disk in self.disks: + name = basename(disk.name) + result.append(['_'.join([name, attrid]), name[:name.index('.')], 'absolute']) + return result + + # Add additional smart attributes to the ORDER. If something goes wrong we don't care. + try: + ORDER.extend(list(set(self.attr.split()) & SMART_ATTR.keys() - set(ORDER))) + except Exception: + pass + self.order = [''.join(['attrid', i]) for i in ORDER] + self.definitions = dict() + units = 'raw' if self.raw_values else 'normalized' + + for k, v in dict([(k, v) for k, v in SMART_ATTR.items() if k in ORDER]).items(): + self.definitions.update({''.join(['attrid', k]): { + 'options': [None, v, units, v, 'smartd.attrid' + k, 'line'], + 'lines': create_lines(k)}}) + +def find_disks_in_log_path(log_path): + # smartd log file is OK if: + # 1. it is a file + # 2. file name endswith with 'csv' + # 3. file is readable + if not isdir(log_path): return None + return [join(log_path, f) for f in listdir(log_path) + if all([isfile(join(log_path, f)), f.endswith('.csv'), access(join(log_path, f), R_OK)])] diff --git a/python.d/tomcat.chart.py b/python.d/tomcat.chart.py index 31f6ab248..c20f85e1e 100644 --- a/python.d/tomcat.chart.py +++ b/python.d/tomcat.chart.py @@ -2,11 +2,13 @@ # Description: tomcat netdata python.d module # Author: Pawel Krupa (paulfantom) -# Python version higher than 2.7 is needed to run this module. - from base import UrlService -import xml.etree.ElementTree as ET # phone home... -#from xml.parsers.expat import errors +from re import compile + +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse # default module values (can be overridden per job in `config`) # update_every = 2 @@ -20,23 +22,23 @@ CHARTS = { 'accesses': { 'options': [None, "Requests", "requests/s", "statistics", "tomcat.accesses", "area"], 'lines': [ - ["accesses", None, 'incremental'] + ["requestCount", 'accesses', 'incremental'] ]}, 'volume': { 'options': [None, "Volume", "KB/s", "volume", "tomcat.volume", "area"], 'lines': [ - ["volume", None, 'incremental', 1, 1024] + ["bytesSent", 'volume', 'incremental', 1, 1024] ]}, 'threads': { 'options': [None, "Threads", "current threads", "statistics", "tomcat.threads", "line"], 'lines': [ - ["current", None, "absolute"], - ["busy", None, "absolute"] + ["currentThreadCount", 'current', "absolute"], + ["currentThreadsBusy", 'busy', "absolute"] ]}, 'jvm': { 'options': [None, "JVM Free Memory", "MB", "statistics", "tomcat.jvm", "area"], 'lines': [ - ["jvm", None, "absolute", 1, 1048576] + ["free", None, "absolute", 1, 1048576] ]} } @@ -44,68 +46,31 @@ CHARTS = { class Service(UrlService): def __init__(self, configuration=None, name=None): UrlService.__init__(self, configuration=configuration, name=name) - if len(self.url) == 0: - self.url = "http://localhost:8080/manager/status?XML=true" + self.url = self.configuration.get('url', "http://127.0.0.1:8080/manager/status?XML=true") self.order = ORDER self.definitions = CHARTS - self.port = 8080 def check(self): - if UrlService.check(self): - return True - - # get port from url - self.port = 0 - for i in self.url.split('/'): - try: - int(i[-1]) - self.port = i.split(':')[-1] - break - except: - pass - if self.port == 0: - self.port = 80 - - test = self._get_data() - if test is None or len(test) == 0: - return False - else: - return True + netloc = urlparse(self.url).netloc.rpartition(':') + if netloc[1] == ':': port = netloc[2] + else: port = 80 + + self.regex_jvm = compile(r'.*?') + self.regex_connector = compile(r'[a-z-]+%s.*?/connector' % port) + self.regex = compile(r'([\w]+)=\\?[\'\"](\d+)\\?[\'\"]') + + return UrlService.check(self) def _get_data(self): """ Format data received from http request :return: dict """ - try: - raw = self._get_raw_data() - try: - data = ET.fromstring(raw) - except ET.ParseError as e: - # if e.code == errors.codes[errors.XML_ERROR_JUNK_AFTER_DOC_ELEMENT]: - if e.code == 9: - end = raw.find('') - end += 9 - raw = raw[:end] - self.debug(raw) - data = ET.fromstring(raw) - else: - raise Exception(e) - - memory = data.find('./jvm/memory') - threads = data.find("./connector[@name='\"http-bio-" + str(self.port) + "\"']/threadInfo") - requests = data.find("./connector[@name='\"http-bio-" + str(self.port) + "\"']/requestInfo") + data = self._get_raw_data() + if data: + jvm = self.regex_jvm.findall(data) or [''] + connector = self.regex_connector.findall(data) or [''] + data = dict(self.regex.findall(''.join([jvm[0], connector[0]]))) + + return data or None - return {'accesses': requests.attrib['requestCount'], - 'volume': requests.attrib['bytesSent'], - 'current': threads.attrib['currentThreadCount'], - 'busy': threads.attrib['currentThreadsBusy'], - 'jvm': memory.attrib['free']} - except (ValueError, AttributeError) as e: - self.debug(str(e)) - return None - except SyntaxError as e: - self.error("Tomcat module needs python 2.7 at least. Stopping") - self.debug(str(e)) - except Exception as e: - self.debug(str(e)) diff --git a/python.d/varnish.chart.py b/python.d/varnish.chart.py index 2b1512f4e..2665bb383 100644 --- a/python.d/varnish.chart.py +++ b/python.d/varnish.chart.py @@ -4,10 +4,8 @@ from base import SimpleService from re import compile -from os import access as is_executable, X_OK from subprocess import Popen, PIPE - # default module values (can be overridden per job in `config`) # update_every = 2 priority = 60000 @@ -80,17 +78,11 @@ CHARTS = {'backend_health': 'options': [None, 'Varnish uptime', 'seconds', 'Uptime', 'varnish.uptime', 'line']} } -DIRECTORIES = ['/bin/', '/usr/bin/', '/sbin/', '/usr/sbin/'] - class Service(SimpleService): def __init__(self, configuration=None, name=None): SimpleService.__init__(self, configuration=configuration, name=name) - try: - self.varnish = [''.join([directory, 'varnishstat']) for directory in DIRECTORIES - if is_executable(''.join([directory, 'varnishstat']), X_OK)][0] - except IndexError: - self.varnish = False + self.varnish = self.find_binary('varnishstat') self.rgx_all = compile(r'([A-Z]+\.)?([\d\w_.]+)\s+(\d+)') # Could be # VBE.boot.super_backend.pipe_hdrbyte (new) @@ -104,7 +96,7 @@ class Service(SimpleService): def check(self): # Cant start without 'varnishstat' command if not self.varnish: - self.error('\'varnishstat\' command was not found in %s or not executable by netdata' % DIRECTORIES) + self.error('Can\'t locate \'varnishstat\' binary or binary is not executable by netdata') return False # If command is present and we can execute it we need to make sure.. @@ -145,7 +137,7 @@ class Service(SimpleService): if not raw_data: return None - return raw_data + return raw_data.decode() def _get_data(self): """ @@ -160,10 +152,10 @@ class Service(SimpleService): return None # 1. ALL data from 'varnishstat -1'. t - type(MAIN, MEMPOOL etc) - to_netdata = {k: int(v) for t, k, v in data_all} + to_netdata = dict([(k, int(v)) for t, k, v in data_all]) # 2. ADD backend statistics - to_netdata.update({'_'.join([n, k]): int(v) for n, k, v in data_backend}) + to_netdata.update(dict([('_'.join([n, k]), int(v)) for n, k, v in data_backend])) # 3. ADD additional keys to dict # 3.1 Cache hit/miss/hitpass OVERALL in percent diff --git a/python.d/web_log.chart.py b/python.d/web_log.chart.py new file mode 100644 index 000000000..cbc8cd235 --- /dev/null +++ b/python.d/web_log.chart.py @@ -0,0 +1,653 @@ +# -*- coding: utf-8 -*- +# Description: web log netdata python.d module +# Author: l2isbad + +from base import LogService +import re +import bisect +from os import access, R_OK +from os.path import getsize +from collections import namedtuple +from copy import deepcopy + +priority = 60000 +retries = 60 + +ORDER = ['response_statuses', 'response_codes', 'bandwidth', 'response_time', 'requests_per_url', 'http_method', + 'http_version', 'requests_per_ipproto', 'clients', 'clients_all'] +CHARTS = { + 'response_codes': { + 'options': [None, 'Response Codes', 'requests/s', 'responses', 'web_log.response_codes', 'stacked'], + 'lines': [ + ['2xx', '2xx', 'incremental'], + ['5xx', '5xx', 'incremental'], + ['3xx', '3xx', 'incremental'], + ['4xx', '4xx', 'incremental'], + ['1xx', '1xx', 'incremental'], + ['0xx', 'other', 'incremental'], + ['unmatched', 'unmatched', 'incremental'] + ]}, + 'bandwidth': { + 'options': [None, 'Bandwidth', 'KB/s', 'bandwidth', 'web_log.bandwidth', 'area'], + 'lines': [ + ['resp_length', 'received', 'incremental', 1, 1024], + ['bytes_sent', 'sent', 'incremental', -1, 1024] + ]}, + 'response_time': { + 'options': [None, 'Processing Time', 'milliseconds', 'timings', 'web_log.response_time', 'area'], + 'lines': [ + ['resp_time_min', 'min', 'incremental', 1, 1000], + ['resp_time_max', 'max', 'incremental', 1, 1000], + ['resp_time_avg', 'avg', 'incremental', 1, 1000] + ]}, + 'clients': { + 'options': [None, 'Current Poll Unique Client IPs', 'unique ips', 'clients', 'web_log.clients', 'stacked'], + 'lines': [ + ['unique_cur_ipv4', 'ipv4', 'incremental', 1, 1], + ['unique_cur_ipv6', 'ipv6', 'incremental', 1, 1] + ]}, + 'clients_all': { + 'options': [None, 'All Time Unique Client IPs', 'unique ips', 'clients', 'web_log.clients_all', 'stacked'], + 'lines': [ + ['unique_tot_ipv4', 'ipv4', 'absolute', 1, 1], + ['unique_tot_ipv6', 'ipv6', 'absolute', 1, 1] + ]}, + 'http_method': { + 'options': [None, 'Requests Per HTTP Method', 'requests/s', 'http methods', 'web_log.http_method', 'stacked'], + 'lines': [ + ['GET', 'GET', 'incremental', 1, 1] + ]}, + 'http_version': { + 'options': [None, 'Requests Per HTTP Version', 'requests/s', 'http versions', + 'web_log.http_version', 'stacked'], + 'lines': []}, + 'requests_per_ipproto': { + 'options': [None, 'Requests Per IP Protocol', 'requests/s', 'ip protocols', 'web_log.requests_per_ipproto', + 'stacked'], + 'lines': [ + ['req_ipv4', 'ipv4', 'incremental', 1, 1], + ['req_ipv6', 'ipv6', 'incremental', 1, 1] + ]}, + 'response_statuses': { + 'options': [None, 'Response Statuses', 'requests/s', 'responses', 'web_log.response_statuses', + 'stacked'], + 'lines': [ + ['successful_requests', 'success', 'incremental', 1, 1], + ['server_errors', 'error', 'incremental', 1, 1], + ['redirects', 'redirect', 'incremental', 1, 1], + ['bad_requests', 'bad', 'incremental', 1, 1], + ['other_requests', 'other', 'incremental', 1, 1] + ]} +} + +NAMED_URL_PATTERN = namedtuple('URL_PATTERN', ['description', 'pattern']) + +DET_RESP_AGGR = ['', '_1xx', '_2xx', '_3xx', '_4xx', '_5xx', '_Other'] + + +class Service(LogService): + def __init__(self, configuration=None, name=None): + """ + :param configuration: + :param name: + # self._get_data = None # will be assigned in 'check' method. + # self.order = None # will be assigned in 'create_*_method' method. + # self.definitions = None # will be assigned in 'create_*_method' method. + """ + LogService.__init__(self, configuration=configuration, name=name) + # Variables from module configuration file + self.log_type = self.configuration.get('type', 'web_access') + self.log_path = self.configuration.get('path') + self.url_pattern = self.configuration.get('categories') # dict + self.custom_log_format = self.configuration.get('custom_log_format') # dict + # Instance variables + self.regex = None # will be assigned in 'find_regex' or 'find_regex_custom' method + self.data = {'bytes_sent': 0, 'resp_length': 0, 'resp_time_min': 0, 'resp_time_max': 0, + 'resp_time_avg': 0, 'unique_cur_ipv4': 0, 'unique_cur_ipv6': 0, '2xx': 0, + '5xx': 0, '3xx': 0, '4xx': 0, '1xx': 0, '0xx': 0, 'unmatched': 0, 'req_ipv4': 0, + 'req_ipv6': 0, 'unique_tot_ipv4': 0, 'unique_tot_ipv6': 0, 'successful_requests': 0, + 'redirects': 0, 'bad_requests': 0, 'server_errors': 0, 'other_requests': 0, 'GET': 0} + + def check(self): + """ + :return: bool + + 1. "log_path" is specified in the module configuration file + 2. "log_path" must be readable by netdata user and must exist + 3. "log_path' must not be empty. We need at least 1 line to find appropriate pattern to parse + 4. other checks depends on log "type" + """ + if not self.log_path: + self.error('log path is not specified') + return False + + if not access(self.log_path, R_OK): + self.error('%s not readable or not exist' % self.log_path) + return False + + if not getsize(self.log_path): + self.error('%s is empty' % self.log_path) + return False + + # Read last line (or first if there is only one line) + with open(self.log_path, 'rb') as logs: + logs.seek(-2, 2) + while logs.read(1) != b'\n': + logs.seek(-2, 1) + if logs.tell() == 0: + break + last_line = logs.readline() + + try: + last_line = last_line.decode() + except UnicodeDecodeError: + try: + last_line = last_line.decode(encoding='utf-8') + except (TypeError, UnicodeDecodeError) as error: + self.error(str(error)) + return False + + if self.log_type == 'web_access': + self.unique_all_time = list() # sorted list of unique IPs + self.detailed_response_codes = self.configuration.get('detailed_response_codes', True) + self.detailed_response_aggregate = self.configuration.get('detailed_response_aggregate', True) + self.all_time = self.configuration.get('all_time', True) + + # Custom_log_format or predefined log format. + if self.custom_log_format: + match_dict, error = self.find_regex_custom(last_line) + else: + match_dict, error = self.find_regex(last_line) + + # "match_dict" is None if there are any problems + if match_dict is None: + self.error(str(error)) + return False + + # self.url_pattern check + if self.url_pattern: + self.url_pattern = check_req_per_url_pattern('rpu', self.url_pattern) + + self.create_access_charts(match_dict) # Create charts + self._get_data = self._get_access_data # _get_data assignment + else: + self.error('Not implemented') + return False + + # Double check + if not self.regex: + self.error('That can not happen, but it happened. "regex" is None') + + self.info('Collected data: %s' % list(match_dict.keys())) + return True + + def find_regex_custom(self, last_line): + """ + :param last_line: str: literally last line from log file + :return: tuple where: + [0]: dict or None: match_dict or None + [1]: str: error description + + We are here only if "custom_log_format" is in logs. We need to make sure: + 1. "custom_log_format" is a dict + 2. "pattern" in "custom_log_format" and pattern is instance + 3. if "time_multiplier" is in "custom_log_format" it must be instance + + If all parameters is ok we need to make sure: + 1. Pattern search is success + 2. Pattern search contains named subgroups (?P) (= "match_dict") + + If pattern search is success we need to make sure: + 1. All mandatory keys ['address', 'code', 'bytes_sent', 'method', 'url'] are in "match_dict" + + If this is True we need to make sure: + 1. All mandatory key values from "match_dict" have the correct format + ("code" is integer, "method" is uppercase word, etc) + + If non mandatory keys in "match_dict" we need to make sure: + 1. All non mandatory key values from match_dict ['resp_length', 'resp_time'] have the correct format + ("resp_length" is integer or "-", "resp_time" is integer or float) + + """ + if not hasattr(self.custom_log_format, 'keys'): + return find_regex_return(msg='Custom log: "custom_log_format" is not a ') + + pattern = self.custom_log_format.get('pattern') + if not (pattern and isinstance(pattern, str)): + return find_regex_return(msg='Custom log: "pattern" option is not specified or type is not ') + + resp_time_func = self.custom_log_format.get('time_multiplier') or 0 + + if not isinstance(resp_time_func, int): + return find_regex_return(msg='Custom log: "time_multiplier" is not an integer') + + try: + regex = re.compile(pattern) + except re.error as error: + return find_regex_return(msg='Pattern compile error: %s' % str(error)) + + match = regex.search(last_line) + if match: + match_dict = match.groupdict() or None + else: + return find_regex_return(msg='Custom log: pattern search FAILED') + + if match_dict is None: + find_regex_return(msg='Custom log: search OK but contains no named subgroups' + ' (you need to use ?P)') + else: + mandatory_dict = {'address': r'[\da-f.:]+', + 'code': r'[1-9]\d{2}', + 'method': r'[A-Z]+', + 'bytes_sent': r'\d+|-'} + optional_dict = {'resp_length': r'\d+', + 'resp_time': r'[\d.]+', + 'http_version': r'\d\.\d'} + + mandatory_values = set(mandatory_dict) - set(match_dict) + if mandatory_values: + return find_regex_return(msg='Custom log: search OK but some mandatory keys (%s) are missing' + % list(mandatory_values)) + else: + for key in mandatory_dict: + if not re.search(mandatory_dict[key], match_dict[key]): + return find_regex_return(msg='Custom log: can\'t parse "%s": %s' + % (key, match_dict[key])) + + optional_values = set(optional_dict) & set(match_dict) + for key in optional_values: + if not re.search(optional_dict[key], match_dict[key]): + return find_regex_return(msg='Custom log: can\'t parse "%s": %s' + % (key, match_dict[key])) + + dot_in_time = '.' in match_dict.get('resp_time', '') + if dot_in_time: + self.resp_time_func = lambda time: time * (resp_time_func or 1000000) + else: + self.resp_time_func = lambda time: time * (resp_time_func or 1) + + self.regex = regex + return find_regex_return(match_dict=match_dict) + + def find_regex(self, last_line): + """ + :param last_line: str: literally last line from log file + :return: tuple where: + [0]: dict or None: match_dict or None + [1]: str: error description + We need to find appropriate pattern for current log file + All logic is do a regex search through the string for all predefined patterns + until we find something or fail. + """ + # REGEX: 1.IPv4 address 2.HTTP method 3. URL 4. Response code + # 5. Bytes sent 6. Response length 7. Response process time + acs_default = re.compile(r'(?P
[\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+|-)') + + acs_apache_ext_insert = re.compile(r'(?P
[\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+|-)' + r' (?P\d+)' + r' (?P\d+) ') + + acs_apache_ext_append = re.compile(r'(?P
[\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+|-)' + r' .*?' + r' (?P\d+)' + r' (?P\d+)' + r'(?: |$)') + + acs_nginx_ext_insert = re.compile(r'(?P
[\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+)' + r' (?P\d+)' + r' (?P\d+\.\d+) ') + + acs_nginx_ext_append = re.compile(r'(?P
[\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+)' + r' .*?' + r' (?P\d+)' + r' (?P\d+\.\d+)') + + def func_usec(time): + return time + + def func_sec(time): + return time * 1000000 + + r_regex = [acs_apache_ext_insert, acs_apache_ext_append, acs_nginx_ext_insert, + acs_nginx_ext_append, acs_default] + r_function = [func_usec, func_usec, func_sec, func_sec, func_usec] + regex_function = zip(r_regex, r_function) + + match_dict = dict() + for regex, function in regex_function: + match = regex.search(last_line) + if match: + self.regex = regex + self.resp_time_func = function + match_dict = match.groupdict() + break + + return find_regex_return(match_dict=match_dict or None, + msg='Unknown log format. You need to use "custom_log_format" feature.') + + def create_access_charts(self, match_dict): + """ + :param match_dict: dict: regex.search.groupdict(). Ex. {'address': '127.0.0.1', 'code': '200', 'method': 'GET'} + :return: + Create additional charts depending on the 'match_dict' keys and configuration file options + 1. 'time_response' chart is removed if there is no 'resp_time' in match_dict. + 2. Other stuff is just remove/add chart depending on yes/no in conf + """ + + def find_job_name(override_name, name): + """ + :param override_name: str: 'name' var from configuration file + :param name: str: 'job_name' from configuration file + :return: str: new job name + We need this for dynamic charts. Actually same logic as in python.d.plugin. + """ + add_to_name = override_name or name + if add_to_name: + return '_'.join(['web_log', re.sub('\s+', '_', add_to_name)]) + else: + return 'web_log' + + self.order = ORDER[:] + self.definitions = deepcopy(CHARTS) + + job_name = find_job_name(self.override_name, self.name) + + self.http_method_chart = 'CHART %s.http_method' \ + ' "" "Requests Per HTTP Method" requests/s "http methods"' \ + ' web_log.http_method stacked 11 %s\n' \ + 'DIMENSION GET GET incremental\n' % (job_name, self.update_every) + self.http_version_chart = 'CHART %s.http_version' \ + ' "" "Requests Per HTTP Version" requests/s "http versions"' \ + ' web_log.http_version stacked 12 %s\n' % (job_name, self.update_every) + + # Remove 'request_time' chart from ORDER if resp_time not in match_dict + if 'resp_time' not in match_dict: + self.order.remove('response_time') + # Remove 'clients_all' chart from ORDER if specified in the configuration + if not self.all_time: + self.order.remove('clients_all') + # Add 'detailed_response_codes' chart if specified in the configuration + if self.detailed_response_codes: + self.detailed_chart = list() + for prio, add_to_dim in enumerate(DET_RESP_AGGR): + self.detailed_chart.append('CHART %s.detailed_response_codes%s ""' + ' "Detailed Response Codes %s" requests/s responses' + ' web_log.detailed_response_codes%s stacked %s %s\n' + % (job_name, add_to_dim, add_to_dim[1:], add_to_dim, + str(prio), self.update_every)) + + codes = DET_RESP_AGGR[:1] if self.detailed_response_aggregate else DET_RESP_AGGR[1:] + for code in codes: + self.order.append('detailed_response_codes%s' % code) + self.definitions['detailed_response_codes%s' % code] = {'options': + [None, + 'Detailed Response Codes %s' % code[1:], + 'requests/s', + 'responses', + 'web_log.detailed_response_codes%s' % code, + 'stacked'], + 'lines': []} + + # Add 'requests_per_url' chart if specified in the configuration + if self.url_pattern: + self.definitions['requests_per_url'] = {'options': [None, 'Requests Per Url', 'requests/s', + 'urls', 'web_log.requests_per_url', 'stacked'], + 'lines': [['rpu_other', 'other', 'incremental']]} + for elem in self.url_pattern: + self.definitions['requests_per_url']['lines'].append([elem.description, elem.description[4:], + 'incremental']) + self.data.update({elem.description: 0}) + self.data.update({'rpu_other': 0}) + else: + self.order.remove('requests_per_url') + + def add_new_dimension(self, dimension, line_list, chart_string, key): + """ + :param dimension: str: response status code. Ex.: '202', '499' + :param line_list: list: Ex.: ['202', '202', 'incremental'] + :param chart_string: Current string we need to pass to netdata to rebuild the chart + :param key: str: CHARTS dict key (chart name). Ex.: 'response_time' + :return: str: new chart string = previous + new dimensions + """ + self.data.update({dimension: 0}) + # SET method check if dim in _dimensions + self._dimensions.append(dimension) + # UPDATE method do SET only if dim in definitions + self.definitions[key]['lines'].append(line_list) + chart = chart_string + chart += "%s %s\n" % ('DIMENSION', ' '.join(line_list)) + print(chart) + return chart + + def _get_access_data(self): + """ + Parse new log lines + :return: dict OR None + None if _get_raw_data method fails. + In all other cases - dict. + """ + raw = self._get_raw_data() + if raw is None: + return None + + request_time, unique_current = list(), list() + request_counter = {'count': 0, 'sum': 0} + ip_address_counter = {'unique_cur_ip': 0} + for line in raw: + match = self.regex.search(line) + if match: + match_dict = match.groupdict() + try: + code = ''.join([match_dict['code'][0], 'xx']) + self.data[code] += 1 + except KeyError: + self.data['0xx'] += 1 + # detailed response code + if self.detailed_response_codes: + self._get_data_detailed_response_codes(match_dict['code']) + # response statuses + self._get_data_statuses(match_dict['code']) + # requests per url + if self.url_pattern: + self._get_data_per_url(match_dict['url']) + # requests per http method + self._get_data_http_method(match_dict['method']) + # requests per http version + if 'http_version' in match_dict: + self._get_data_http_version(match_dict['http_version']) + # bandwidth sent + bytes_sent = match_dict['bytes_sent'] if '-' not in match_dict['bytes_sent'] else 0 + self.data['bytes_sent'] += int(bytes_sent) + # request processing time and bandwidth received + if 'resp_length' in match_dict: + self.data['resp_length'] += int(match_dict['resp_length']) + if 'resp_time' in match_dict: + resp_time = self.resp_time_func(float(match_dict['resp_time'])) + bisect.insort_left(request_time, resp_time) + request_counter['count'] += 1 + request_counter['sum'] += resp_time + # requests per ip proto + proto = 'ipv4' if '.' in match_dict['address'] else 'ipv6' + self.data['req_' + proto] += 1 + # unique clients ips + if address_not_in_pool(self.unique_all_time, match_dict['address'], + self.data['unique_tot_ipv4'] + self.data['unique_tot_ipv6']): + self.data['unique_tot_' + proto] += 1 + if address_not_in_pool(unique_current, match_dict['address'], ip_address_counter['unique_cur_ip']): + self.data['unique_cur_' + proto] += 1 + ip_address_counter['unique_cur_ip'] += 1 + else: + self.data['unmatched'] += 1 + + # timings + if request_time: + self.data['resp_time_min'] += int(request_time[0]) + self.data['resp_time_avg'] += int(round(float(request_counter['sum']) / request_counter['count'])) + self.data['resp_time_max'] += int(request_time[-1]) + return self.data + + def _get_data_detailed_response_codes(self, code): + """ + :param code: str: CODE from parsed line. Ex.: '202, '499' + :return: + Calls add_new_dimension method If the value is found for the first time + """ + if code not in self.data: + if self.detailed_response_aggregate: + chart_string_copy = self.detailed_chart[0] + self.detailed_chart[0] = self.add_new_dimension(code, [code, code, 'incremental'], + chart_string_copy, 'detailed_response_codes') + else: + code_index = int(code[0]) if int(code[0]) < 6 else 6 + chart_string_copy = self.detailed_chart[code_index] + chart_name = 'detailed_response_codes' + DET_RESP_AGGR[code_index] + self.detailed_chart[code_index] = self.add_new_dimension(code, [code, code, 'incremental'], + chart_string_copy, chart_name) + self.data[code] += 1 + + def _get_data_http_method(self, method): + """ + :param method: str: METHOD from parsed line. Ex.: 'GET', 'POST' + :return: + Calls add_new_dimension method If the value is found for the first time + """ + if method not in self.data: + chart_string_copy = self.http_method_chart + self.http_method_chart = self.add_new_dimension(method, [method, method, 'incremental'], + chart_string_copy, 'http_method') + self.data[method] += 1 + + def _get_data_http_version(self, http_version): + """ + :param http_version: str: METHOD from parsed line. Ex.: '1.1', '1.0' + :return: + Calls add_new_dimension method If the value is found for the first time + """ + http_version_dim_id = http_version.replace('.', '_') + if http_version_dim_id not in self.data: + chart_string_copy = self.http_version_chart + self.http_version_chart = self.add_new_dimension(http_version_dim_id, + [http_version_dim_id, http_version, 'incremental'], + chart_string_copy, 'http_version') + self.data[http_version_dim_id] += 1 + + def _get_data_per_url(self, url): + """ + :param url: str: URL from parsed line + :return: + Scan through string looking for the first location where patterns produce a match for all user + defined patterns + """ + match = None + for elem in self.url_pattern: + if elem.pattern.search(url): + self.data[elem.description] += 1 + match = True + break + if not match: + self.data['rpu_other'] += 1 + + def _get_data_statuses(self, code): + """ + :param code: str: response status code. Ex.: '202', '499' + :return: + """ + code_class = code[0] + if code_class == '2' or code == '304' or code_class == '1': + self.data['successful_requests'] += 1 + elif code_class == '3': + self.data['redirects'] += 1 + elif code_class == '4': + self.data['bad_requests'] += 1 + elif code_class == '5': + self.data['server_errors'] += 1 + else: + self.data['other_requests'] += 1 + + +def address_not_in_pool(pool, address, pool_size): + """ + :param pool: list of ip addresses + :param address: ip address + :param pool_size: current pool size + :return: True if address not in pool. False if address in pool. + """ + index = bisect.bisect_left(pool, address) + if index < pool_size: + if pool[index] == address: + return False + else: + bisect.insort_left(pool, address) + return True + else: + bisect.insort_left(pool, address) + return True + + +def find_regex_return(match_dict=None, msg='Generic error message'): + """ + :param match_dict: dict: re.search.groupdict() or None + :param msg: str: error description + :return: tuple: + """ + return match_dict, msg + + +def check_req_per_url_pattern(string, url_pattern): + """ + :param string: str: + :param url_pattern: dict: ex. {'dim1': 'pattern1>', 'dim2': ''} + :return: list of named tuples or None: + We need to make sure all patterns are valid regular expressions + """ + if not hasattr(url_pattern, 'keys'): + return None + + result = list() + + def is_valid_pattern(pattern): + """ + :param pattern: str + :return: re.compile(pattern) or None + """ + if not isinstance(pattern, str): + return False + else: + try: + compile_pattern = re.compile(pattern) + except re.error: + return False + else: + return compile_pattern + + for dimension, regex in url_pattern.items(): + valid_pattern = is_valid_pattern(regex) + if isinstance(dimension, str) and valid_pattern: + result.append(NAMED_URL_PATTERN(description='_'.join([string, dimension]), pattern=valid_pattern)) + + return result or None 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 + * Released under GPL v3+ + */ + #include "common.h" +#ifdef __FreeBSD__ +#include +#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 -void netdata_cleanup_and_exit(int ret) { - exit(ret); -} +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; + + +// 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 target *apps_groups_root_target = NULL; -struct target *apps_groups_default_target = NULL; -long apps_groups_targets = 0; +struct pid_stat { + int32_t pid; + char comm[MAX_COMPARE_NAME + 1]; + char cmdline[MAX_CMDLINE + 1]; -struct target *users_root_target = NULL; -struct target *groups_root_target = NULL; + uint32_t log_thrown; -static struct target *get_users_target(uid_t uid) -{ + // char state; + int32_t ppid; + // int32_t pgrp; + // int32_t session; + // int32_t tty_nr; + // int32_t tpgid; + // uint64_t flags; + + // these are raw values collected + 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); + + 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(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; + // 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(all_pids[pid]->fds) freez(all_pids[pid]->fds); - if(all_pids[pid]->stat_filename) freez(all_pids[pid]->stat_filename); - if(all_pids[pid]->statm_filename) freez(all_pids[pid]->statm_filename); - if(all_pids[pid]->io_filename) freez(all_pids[pid]->io_filename); - if(all_pids[pid]->cmdline_filename) freez(all_pids[pid]->cmdline_filename); - freez(all_pids[pid]); + if(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)) + ))) { - all_pids[pid] = NULL; - all_pids_count--; + 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//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); - - 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); + p->io_collected_usec = now_monotonic_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++; - - unsigned long long last; + collected_usec = now_monotonic_usec(); - 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); + calls_counter++; - // 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); + // temporary - it is added global_ntime; + kernel_uint_t global_ntime = 0; - 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; @@ -908,35 +1234,15 @@ static inline int read_proc_stat() { return 1; -cleanup: - global_utime = 0; - global_stime = 0; - global_gtime = 0; - return 0; -} - - -// ---------------------------------------------------------------------------- -// 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; +cleanup: + global_utime = 0; + global_stime = 0; + global_gtime = 0; + return 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)); + + // 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 - // if another process already has this, we will get - // the same id - p->fds[fdid] = file_descriptor_find_or_add(linkname); + 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//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//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//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//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//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; + + 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 - long slc = 0; + 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 \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); } } diff --git a/src/avl.c b/src/avl.c index 1ec1b8ad2..0ea119a7c 100644 --- a/src/avl.c +++ b/src/avl.c @@ -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) diff --git a/src/avl.h b/src/avl.h index d30be0dd8..19648cd1d 100644 --- a/src/avl.h +++ b/src/avl.h @@ -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; @@ -1152,36 +1165,42 @@ pid_t get_system_pid_max(void) { // As we currently do not know a solution to query pid_max from the os // 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 #include -#if !(defined(__FreeBSD__) || defined(__APPLE__)) +#ifdef HAVE_SYS_PRCTL_H #include -#endif /* __FreeBSD__ || __APPLE__*/ +#endif #include #include @@ -109,6 +109,10 @@ #include #endif +#ifdef HAVE_CAPABILITY +#include +#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 #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 -// NEEDED BY: struct devstat #include -// NEEDED BY: struct xswdev +#include #include -// NEEDED BY: struct semid_kernel, struct shmid_kernel, struct msqid_kernel + #define _KERNEL #include #include #include #undef _KERNEL -// NEEDED BY: struct sysctl_netisr_workstream, struct sysctl_netisr_work + #include -// NEEDED BY: struct ifaddrs, getifaddrs() #include #include -// NEEDED BY do_tcp... -#include -#include -// NEEDED BY do_udp..., do_ip... -#include -// NEEDED BY do_udp... -#include -#include -// NEEDED BY do_icmp... + #include +#include #include #include -// NEEDED BY do_ip6... #include -// NEEDED BY do_icmp6... #include -// NEEDED BY do_space, do_inodes -#include -// NEEDED BY do_uptime -#include +#include +#include +#include +#include + +// -------------------------------------------------------------------------------------------------------------------- +// 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) - -int do_freebsd_sysctl(int update_every, usec_t dt) { - (void)dt; +// -------------------------------------------------------------------------------------------------------------------- +// FreeBSD plugin initialization - 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 - 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"); +int do_kern_cp_times(int update_every, usec_t dt) { + (void)dt; + + 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); + return 0; +} - rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); +// -------------------------------------------------------------------------------------------------------------------- +// net.inet.tcp.states - rrddim_set(st, "InOctets", iftot.ift_ibytes); - rrddim_set(st, "OutOctets", iftot.ift_obytes); - rrdset_done(st); +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]; - // -------------------------------------------------------------------- + // 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 { - 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); + static RRDSET *st = NULL; + static RRDDIM *rd = NULL; - rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + 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); + } - rrddim_set(st, "sent", iftot.ift_obytes); - rrddim_set(st, "received", iftot.ift_ibytes); - rrdset_done(st); + return 0; +} - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr->sa_family != AF_LINK) - continue; +// -------------------------------------------------------------------------------------------------------------------- +// net.inet.tcp.stats - // -------------------------------------------------------------------- +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); + } - 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); + // 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_syncookies || do_ecn)) { + static int mib[4] = {0, 0, 0, 0}; + struct tcpstat tcpstat; - rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL); - rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL); - } - else rrdset_next(st); + if (unlikely(GETSYSCTL_SIMPLE("net.inet.tcp.stats", mib, tcpstat))) { + do_tcp_packets = 0; + error("DISABLED: ipv4.tcppackets chart"); + do_tcp_errors = 0; + error("DISABLED: ipv4.tcperrors chart"); + do_tcp_handshake = 0; + error("DISABLED: ipv4.tcphandshake chart"); + do_tcpext_connaborts = 0; + error("DISABLED: ipv4.tcpconnaborts chart"); + do_tcpext_ofo = 0; + error("DISABLED: ipv4.tcpofo chart"); + do_tcpext_syncookies = 0; + error("DISABLED: ipv4.tcpsyncookies chart"); + do_ecn = 0; + error("DISABLED: ipv4.ecnpkts chart"); + error("DISABLED: net.inet.tcp.stats module"); + return 1; + } else { - rrddim_set(st, "received", IFA_DATA(ibytes)); - rrddim_set(st, "sent", IFA_DATA(obytes)); - rrdset_done(st); + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + if (likely(do_tcp_packets)) { + static RRDSET *st = NULL; + static RRDDIM *rd_in_segs = NULL, *rd_out_segs = NULL; - 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); + 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, "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)); + 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); + } - // -------------------------------------------------------------------- - - 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); + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + if (likely(do_tcp_errors)) { + static RRDSET *st = NULL; + static RRDDIM *rd_in_errs = NULL, *rd_in_csum_errs = NULL, *rd_retrans_segs = NULL; - 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; + 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); - rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL); #if __FreeBSD__ >= 11 - rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL); + rrddim_set_by_pointer(st, rd_in_errs, tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvreassfull + + tcpstat.tcps_rcvshort); +#else + rrddim_set_by_pointer(st, rd_in_errs, tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvshort); #endif - } - else rrdset_next(st); - - rrddim_set(st, "inbound", IFA_DATA(iqdrops)); -#if __FreeBSD__ >= 11 - rrddim_set(st, "outbound", IFA_DATA(oqdrops)); -#endif - rrdset_done(st); - - // -------------------------------------------------------------------- - - 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; - - 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)); + 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); } - freeifaddrs(ifap); - } - } + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + if (likely(do_tcp_handshake)) { + static RRDSET *st = NULL; + static RRDDIM *rd_estab_resets = NULL, *rd_active_opens = NULL, *rd_passive_opens = NULL, + *rd_attempt_fails = NULL; - // 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); - - rrddim_add(st, "CurrEstab", "connections", 1, 1, RRDDIM_ABSOLUTE); + 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, "CurrEstab", tcps_states[TCPS_ESTABLISHED]); + 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); } - } - } - - // -------------------------------------------------------------------- - // 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))){ - do_tcp_packets = 0; - error("DISABLED: ipv4.tcppackets"); - do_tcp_errors = 0; - error("DISABLED: ipv4.tcperrors"); - do_tcp_handshake = 0; - error("DISABLED: ipv4.tcphandshake"); - do_tcpext_connaborts = 0; - error("DISABLED: ipv4.tcpconnaborts"); - do_tcpext_ofo = 0; - error("DISABLED: ipv4.tcpofo"); - do_tcpext_syscookies = 0; - error("DISABLED: ipv4.tcpsyncookies"); - do_ecn = 0; - error("DISABLED: ipv4.ecnpkts"); - } 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); - - rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL); - } else - rrdset_next(st); + // -------------------------------------------------------------------- - rrddim_set(st, "InSegs", tcpstat.tcps_rcvtotal); - rrddim_set(st, "OutSegs", tcpstat.tcps_sndtotal); - rrdset_done(st); - } + 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; - // -------------------------------------------------------------------- + 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 (likely(do_tcp_errors)) { - st = rrdset_find("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); - } else - rrdset_next(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); -#if __FreeBSD__ >= 11 - rrddim_set(st, "InErrs", tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvreassfull + tcpstat.tcps_rcvshort); -#else - rrddim_set(st, "InErrs", 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_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 (likely(do_tcp_handshake)) { - st = rrdset_find("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); - } else - rrdset_next(st); + if (do_tcpext_ofo == CONFIG_BOOLEAN_YES || (do_tcpext_ofo == CONFIG_BOOLEAN_AUTO && tcpstat.tcps_rcvoopack)) { + do_tcpext_ofo = CONFIG_BOOLEAN_YES; - 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); - rrdset_done(st); - } - - // -------------------------------------------------------------------- + static RRDSET *st = NULL; + static RRDDIM *rd_ofo_queue = NULL; - 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); - - 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); + 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, "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_ofo_queue, tcpstat.tcps_rcvoopack); 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_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; + + static RRDSET *st = NULL; + static RRDDIM *rd_recv = NULL, *rd_send = NULL, *rd_failed = NULL; - rrddim_add(st, "TCPOFOQueue", "inqueue", 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, "TCPOFOQueue", tcpstat.tcps_rcvoopack); + 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_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_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; - 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_ce = NULL, *rd_no_ect = NULL, *rd_ect0 = NULL, *rd_ect1 = 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", + "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, "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_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; + } - 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; + return 0; +} - 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); - } - else rrdset_next(st); +// -------------------------------------------------------------------------------------------------------------------- +// net.inet.udp.stats - 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); - rrdset_done(st); - } +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 - if (likely(do_icmp_packets || do_icmpmsg)) { - if (unlikely(GETSYSCTL("net.inet.icmp.stats", icmpstat))) { +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_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; + 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, "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); - - 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; + + static RRDSET *st = NULL; + static RRDDIM *rd_ok = NULL, *rd_failed = NULL, *rd_all = NULL; - 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); + 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; - - rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL); - rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL); + do_ip6_errors = CONFIG_BOOLEAN_YES; - 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); + 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, "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; + + static RRDSET *st = NULL; + static RRDDIM *rd_in = NULL, *rd_out = NULL, *rd_in_replies = NULL, *rd_out_replies = NULL; - 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); + 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; - 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", + "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 + +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 (likely(do_uptime)) { - if (unlikely(GETSYSCTL("kern.boottime", boot_time))) { - do_uptime = 0; - error("DISABLED: system.uptime"); + 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 + * UCRL-CODE-222073 + */ + +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_FREEIPMI + +#include +#include + +/* 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 \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 old mode 100755 new mode 100644 index 193312eec..46b27db6f --- a/src/health.c +++ b/src/health.c @@ -1,2588 +1,84 @@ +#define NETDATA_HEALTH_INTERNALS #include "common.h" -#define RRDVAR_MAX_LENGTH 1024 +int default_health_enabled = 1; -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); -} - -// ---------------------------------------------------------------------------- -// 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; - } - - 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) { - 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); -} +// ---------------------------------------------------------------------------- +// health initialization -static inline char *health_config_dir(void) { +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 - - 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 there is calculation expression, run it - 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); - - if(unlikely(runnable && !netdata_exit)) { - rrdhost_rdlock(&localhost); + rrdhost_unlock(host); - for(rc = localhost.alarms; rc; rc = rc->next) { - if(unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE))) - continue; + if(unlikely(runnable && !netdata_exit)) { + rrdhost_rdlock(host); - int warning_status = RRDCALC_STATUS_UNDEFINED; - int critical_status = RRDCALC_STATUS_UNDEFINED; + for(rc = host->alarms; rc; rc = rc->next) { + if(unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_RUNNABLE))) + continue; - if(likely(rc->warning)) { - if(unlikely(!expression_evaluate(rc->warning))) { - // calculation failed + int warning_status = RRDCALC_STATUS_UNDEFINED; + int critical_status = RRDCALC_STATUS_UNDEFINED; - 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)); + // -------------------------------------------------------- + // check the warning expression - 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 - - 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)); + // -------------------------------------------------------- + // check the critical expression - 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 */ - if (unlikely(pthread_setcancelstate(oldstate, NULL) != 0)) + rrd_unlock(); + + 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]; diff --git a/src/ipc.c b/src/ipc.c index a5ab342d3..1dabf5e19 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -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 diff --git a/src/log.c b/src/log.c index d4c7fa14d..855ecaee6 100644 --- a/src/log.c +++ b/src/log.c @@ -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 ); diff --git a/src/log.h b/src/log.h index e61ffdd08..d8ff0654b 100644 --- a/src/log.h +++ b/src/log.h @@ -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 \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 -#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 -#include -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 + +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 - 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:"", 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:""); - 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:""); - 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:""); + 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; diff --git a/src/rrd.c b/src/rrd.c index bd0175efc..a9ff6243d 100644 --- a/src/rrd.c +++ b/src/rrd.c @@ -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:""); - 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); -} diff --git a/src/rrd.h b/src/rrd.h index dbaf98650..2f4f2127f 100644 --- a/src/rrd.h +++ b/src/rrd.h @@ -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:""); + 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,1027 +567,37 @@ 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) { - 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"); +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_strcat(w->response.data, "Invalid registry Switch request."); + buffer_strcat(w->response.data, "This host does not maintain a database"); 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; - } -} - -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) -{ - // get the api version - char *tok = mystrsep(&url, "/?&"); - if(tok && *tok) { - debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok); - if(strcmp(tok, "v1") == 0) - return web_client_api_request_v1(w, url); - else { - buffer_flush(w->response.data); - buffer_strcat(w->response.data, "Unsupported API version: "); - buffer_strcat_htmlescape(w->response.data, tok); - return 404; - } - } - else { - buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Which API version?"); - return 400; - } -} - -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, "});"); + return func(host, w, 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(host, w, url); 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); + w->response.data->contenttype = CT_TEXT_HTML; + buffer_strcat(w->response.data, "Unsupported API version: "); + buffer_strcat_htmlescape(w->response.data, tok); + return 404; } } - - return 200; + else { + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Which API version?"); + return 400; + } } const char *web_content_type_to_string(uint8_t contenttype) { @@ -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); diff --git a/system/Makefile.in b/system/Makefile.in index 749b99f4b..aa0a60e02 100644 --- a/system/Makefile.in +++ b/system/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. @@ -15,61 +16,6 @@ @SET_MAKE@ 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@ @@ -88,10 +34,12 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ +DIST_COMMON = $(dist_noinst_DATA) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(top_srcdir)/build/subst.inc subdir = system 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 \ @@ -100,38 +48,16 @@ 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_noinst_DATA) \ - $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = -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 = SOURCES = DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac DATA = $(dist_noinst_DATA) $(nodist_noinst_DATA) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/build/subst.inc DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -155,7 +81,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@ @@ -169,6 +99,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@ @@ -312,6 +246,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu system/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu system/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -320,7 +255,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; -$(top_srcdir)/build/subst.inc $(am__empty): +$(top_srcdir)/build/subst.inc: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh @@ -330,11 +265,11 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -tags TAGS: - -ctags CTAGS: +tags: TAGS +TAGS: -cscope cscopelist: +ctags: CTAGS +CTAGS: distdir: $(DISTFILES) @@ -471,18 +406,15 @@ uninstall-am: .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ - ctags-am distclean distclean-generic distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am 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-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ - pdf-am ps ps-am tags-am uninstall uninstall-am - -.PRECIOUS: Makefile +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am 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-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am .in: if sed \ diff --git a/system/netdata.service.in b/system/netdata.service.in index e260e2738..6bbb84eb5 100644 --- a/system/netdata.service.in +++ b/system/netdata.service.in @@ -1,6 +1,6 @@ [Unit] Description=Real time performance monitoring -After=network.target httpd.service squid.service nfs-server.service mysqld.service named.service postfix.service +After=network.target httpd.service squid.service nfs-server.service mysqld.service mysql.service named.service postfix.service [Service] Type=simple diff --git a/web/Makefile.am b/web/Makefile.am index 396cf0be8..03a487597 100644 --- a/web/Makefile.am +++ b/web/Makefile.am @@ -11,6 +11,7 @@ dist_web_DATA = \ dashboard.html \ dashboard.js \ dashboard_info.js \ + dashboard_info_custom_example.js \ dashboard.css \ dashboard.slate.css \ favicon.ico \ @@ -44,7 +45,7 @@ dist_weblib_DATA = \ lib/d3-3.5.17.min.js \ lib/dygraph-combined-dd74404.js \ lib/dygraph-smooth-plotter-dd74404.js \ - lib/gauge-d5260c3.min.js \ + lib/gauge-1.3.2.min.js \ lib/jquery-2.2.4.min.js \ lib/jquery.easypiechart-97b5824.min.js \ lib/perfect-scrollbar-0.6.15.min.js \ diff --git a/web/Makefile.in b/web/Makefile.in index e09392e9e..9ec69e6aa 100644 --- a/web/Makefile.in +++ b/web/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. @@ -15,61 +16,6 @@ @SET_MAKE@ 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@ @@ -89,9 +35,14 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = web +DIST_COMMON = $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ + $(dist_webfonts_DATA) $(dist_webimages_DATA) \ + $(dist_weblib_DATA) $(dist_webold_DATA) \ + $(dist_webwellknown_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 \ @@ -100,33 +51,12 @@ 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_web_DATA) \ - $(dist_webcss_DATA) $(dist_webdnt_DATA) $(dist_webfonts_DATA) \ - $(dist_webimages_DATA) $(dist_weblib_DATA) $(dist_webold_DATA) \ - $(dist_webwellknown_DATA) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = -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 = SOURCES = DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -162,12 +92,9 @@ DATA = $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ $(dist_webfonts_DATA) $(dist_webimages_DATA) \ $(dist_weblib_DATA) $(dist_webold_DATA) \ $(dist_webwellknown_DATA) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -191,7 +118,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@ @@ -205,6 +136,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@ @@ -314,6 +249,7 @@ dist_web_DATA = \ dashboard.html \ dashboard.js \ dashboard_info.js \ + dashboard_info_custom_example.js \ dashboard.css \ dashboard.slate.css \ favicon.ico \ @@ -347,7 +283,7 @@ dist_weblib_DATA = \ lib/d3-3.5.17.min.js \ lib/dygraph-combined-dd74404.js \ lib/dygraph-smooth-plotter-dd74404.js \ - lib/gauge-d5260c3.min.js \ + lib/gauge-1.3.2.min.js \ lib/jquery-2.2.4.min.js \ lib/jquery.easypiechart-97b5824.min.js \ lib/perfect-scrollbar-0.6.15.min.js \ @@ -432,6 +368,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu web/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu web/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -451,11 +388,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_webDATA: $(dist_web_DATA) @$(NORMAL_INSTALL) + test -z "$(webdir)" || $(MKDIR_P) "$(DESTDIR)$(webdir)" @list='$(dist_web_DATA)'; test -n "$(webdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(webdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(webdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -472,11 +406,8 @@ uninstall-dist_webDATA: dir='$(DESTDIR)$(webdir)'; $(am__uninstall_files_from_dir) install-dist_webcssDATA: $(dist_webcss_DATA) @$(NORMAL_INSTALL) + test -z "$(webcssdir)" || $(MKDIR_P) "$(DESTDIR)$(webcssdir)" @list='$(dist_webcss_DATA)'; test -n "$(webcssdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(webcssdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(webcssdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -493,11 +424,8 @@ uninstall-dist_webcssDATA: dir='$(DESTDIR)$(webcssdir)'; $(am__uninstall_files_from_dir) install-dist_webdntDATA: $(dist_webdnt_DATA) @$(NORMAL_INSTALL) + test -z "$(webdntdir)" || $(MKDIR_P) "$(DESTDIR)$(webdntdir)" @list='$(dist_webdnt_DATA)'; test -n "$(webdntdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(webdntdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(webdntdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -514,11 +442,8 @@ uninstall-dist_webdntDATA: dir='$(DESTDIR)$(webdntdir)'; $(am__uninstall_files_from_dir) install-dist_webfontsDATA: $(dist_webfonts_DATA) @$(NORMAL_INSTALL) + test -z "$(webfontsdir)" || $(MKDIR_P) "$(DESTDIR)$(webfontsdir)" @list='$(dist_webfonts_DATA)'; test -n "$(webfontsdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(webfontsdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(webfontsdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -535,11 +460,8 @@ uninstall-dist_webfontsDATA: dir='$(DESTDIR)$(webfontsdir)'; $(am__uninstall_files_from_dir) install-dist_webimagesDATA: $(dist_webimages_DATA) @$(NORMAL_INSTALL) + test -z "$(webimagesdir)" || $(MKDIR_P) "$(DESTDIR)$(webimagesdir)" @list='$(dist_webimages_DATA)'; test -n "$(webimagesdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(webimagesdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(webimagesdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -556,11 +478,8 @@ uninstall-dist_webimagesDATA: dir='$(DESTDIR)$(webimagesdir)'; $(am__uninstall_files_from_dir) install-dist_weblibDATA: $(dist_weblib_DATA) @$(NORMAL_INSTALL) + test -z "$(weblibdir)" || $(MKDIR_P) "$(DESTDIR)$(weblibdir)" @list='$(dist_weblib_DATA)'; test -n "$(weblibdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(weblibdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(weblibdir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -577,11 +496,8 @@ uninstall-dist_weblibDATA: dir='$(DESTDIR)$(weblibdir)'; $(am__uninstall_files_from_dir) install-dist_weboldDATA: $(dist_webold_DATA) @$(NORMAL_INSTALL) + test -z "$(webolddir)" || $(MKDIR_P) "$(DESTDIR)$(webolddir)" @list='$(dist_webold_DATA)'; test -n "$(webolddir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(webolddir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(webolddir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -598,11 +514,8 @@ uninstall-dist_weboldDATA: dir='$(DESTDIR)$(webolddir)'; $(am__uninstall_files_from_dir) install-dist_webwellknownDATA: $(dist_webwellknown_DATA) @$(NORMAL_INSTALL) + test -z "$(webwellknowndir)" || $(MKDIR_P) "$(DESTDIR)$(webwellknowndir)" @list='$(dist_webwellknown_DATA)'; test -n "$(webwellknowndir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(webwellknowndir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(webwellknowndir)" || exit 1; \ - fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -617,11 +530,11 @@ uninstall-dist_webwellknownDATA: @list='$(dist_webwellknown_DATA)'; test -n "$(webwellknowndir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(webwellknowndir)'; $(am__uninstall_files_from_dir) -tags TAGS: - -ctags CTAGS: +tags: TAGS +TAGS: -cscope cscopelist: +ctags: CTAGS +CTAGS: distdir: $(DISTFILES) @@ -766,10 +679,10 @@ uninstall-am: uninstall-dist_webDATA uninstall-dist_webcssDATA \ .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ - ctags-am distclean distclean-generic distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dist_webDATA install-dist_webcssDATA \ +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-dist_webDATA install-dist_webcssDATA \ install-dist_webdntDATA install-dist_webfontsDATA \ install-dist_webimagesDATA install-dist_weblibDATA \ install-dist_weboldDATA install-dist_webwellknownDATA \ @@ -778,14 +691,11 @@ uninstall-am: uninstall-dist_webDATA uninstall-dist_webcssDATA \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags-am \ - uninstall uninstall-am uninstall-dist_webDATA \ - uninstall-dist_webcssDATA uninstall-dist_webdntDATA \ - uninstall-dist_webfontsDATA uninstall-dist_webimagesDATA \ - uninstall-dist_weblibDATA uninstall-dist_weboldDATA \ - uninstall-dist_webwellknownDATA - -.PRECIOUS: Makefile + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am uninstall-dist_webDATA uninstall-dist_webcssDATA \ + uninstall-dist_webdntDATA uninstall-dist_webfontsDATA \ + uninstall-dist_webimagesDATA uninstall-dist_weblibDATA \ + uninstall-dist_weboldDATA uninstall-dist_webwellknownDATA version.txt: diff --git a/web/dashboard.html b/web/dashboard.html index 342374c8d..75de65ad9 100644 --- a/web/dashboard.html +++ b/web/dashboard.html @@ -13,7 +13,7 @@ - + diff --git a/web/dashboard.js b/web/dashboard.js index 6fc294204..b34accdec 100644 --- a/web/dashboard.js +++ b/web/dashboard.js @@ -1,35 +1,54 @@ +// ---------------------------------------------------------------------------- // You can set the following variables before loading this script: -// -// var netdataNoDygraphs = true; // do not use dygraph -// var netdataNoSparklines = true; // do not use sparkline -// var netdataNoPeitys = true; // do not use peity -// var netdataNoGoogleCharts = true; // do not use google -// var netdataNoMorris = true; // do not use morris -// var netdataNoEasyPieChart = true; // do not use easy pie chart -// var netdataNoGauge = true; // do not use gauge.js -// var netdataNoD3 = true; // do not use D3 -// var netdataNoC3 = true; // do not use C3 -// var netdataNoBootstrap = true; // do not load bootstrap -// var netdataDontStart = true; // do not start the thread to process the charts -// var netdataErrorCallback = null; // Callback function that will be invoked upon error -// var netdataRegistry = true; // Update the registry (default disabled) -// var netdataRegistryCallback = null; // Callback function that will be invoked with one param, -// the URLs from the registry -// var netdataShowHelp = false; // enable/disable help (default enabled) -// var netdataShowAlarms = true; // enable/disable alarms checks and notifications (default disabled) -// -// var netdataRegistryAfterMs = 1500 // the time to consult to registry on startup -// -// var netdataCallback = null; // a function to call when netdata is ready -// // netdata will be running while this is called (call NETDATA.pause to stop it) -// var netdataPrepCallback = null; // a callback to be called before netdata does anything else -// -// You can also set the default netdata server, using the following. -// When this variable is not set, we assume the page is hosted on your -// netdata server already. -// var netdataServer = "http://yourhost:19999"; // set your NetData server +/*global netdataNoDygraphs *//* boolean, disable dygraph charts + * (default: false) */ +/*global netdataNoSparklines *//* boolean, disable sparkline charts + * (default: false) */ +/*global netdataNoPeitys *//* boolean, disable peity charts + * (default: false) */ +/*global netdataNoGoogleCharts *//* boolean, disable google charts + * (default: false) */ +/*global netdataNoMorris *//* boolean, disable morris charts + * (default: false) */ +/*global netdataNoEasyPieChart *//* boolean, disable easypiechart charts + * (default: false) */ +/*global netdataNoGauge *//* boolean, disable gauge.js charts + * (default: false) */ +/*global netdataNoD3 *//* boolean, disable d3 charts + * (default: false) */ +/*global netdataNoC3 *//* boolean, disable c3 charts + * (default: false) */ +/*global netdataNoBootstrap *//* boolean, disable bootstrap - disables help too + * (default: false) */ +/*global netdataDontStart *//* boolean, do not start the thread to process the charts + * (default: false) */ +/*global netdataErrorCallback *//* function, callback to be called when the dashboard encounters an error + * (default: null) */ +/*global netdataRegistry:true *//* boolean, use the netdata registry + * (default: false) */ +/*global netdataNoRegistry *//* boolean, included only for compatibility with existing custom dashboard + * (obsolete - do not use this any more) */ +/*global netdataRegistryCallback *//* function, callback that will be invoked with one param: the URLs from the registry + * (default: null) */ +/*global netdataShowHelp:true *//* boolean, disable charts help + * (default: true) */ +/*global netdataShowAlarms:true *//* boolean, enable alarms checks and notifications + * (default: false) */ +/*global netdataRegistryAfterMs:true *//* ms, delay registry use at started + * (default: 1500) */ +/*global netdataCallback *//* function, callback to be called when netdata is ready to start + * (default: null) + * netdata will be running while this is called + * (call NETDATA.pause to stop it) */ +/*global netdataPrepCallback *//* function, callback to be called before netdata does anything else + * (default: null) */ +/*global netdataServer *//* string, the URL of the netdata server to use + * (default: the URL the page is hosted at) */ + +// ---------------------------------------------------------------------------- // global namespace + var NETDATA = window.NETDATA || {}; (function(window, document) { @@ -111,7 +130,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.peity_js = NETDATA.serverDefault + 'lib/jquery.peity-3.2.0.min.js'; NETDATA.sparkline_js = NETDATA.serverDefault + 'lib/jquery.sparkline-2.1.2.min.js'; NETDATA.easypiechart_js = NETDATA.serverDefault + 'lib/jquery.easypiechart-97b5824.min.js'; - NETDATA.gauge_js = NETDATA.serverDefault + 'lib/gauge-d5260c3.min.js'; + NETDATA.gauge_js = NETDATA.serverDefault + 'lib/gauge-1.3.2.min.js'; NETDATA.dygraph_js = NETDATA.serverDefault + 'lib/dygraph-combined-dd74404.js'; NETDATA.dygraph_smooth_js = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter-dd74404.js'; NETDATA.raphael_js = NETDATA.serverDefault + 'lib/raphael-2.2.4-min.js'; @@ -199,6 +218,29 @@ var NETDATA = window.NETDATA || {}; if(netdataRegistry === false && typeof netdataRegistryCallback === 'function') netdataRegistry = true; + + // ---------------------------------------------------------------------------------------------------------------- + // detect if this is probably a slow device + + var isSlowDeviceResult = undefined; + var isSlowDevice = function() { + if(isSlowDeviceResult !== undefined) + return isSlowDeviceResult; + + try { + var ua = navigator.userAgent.toLowerCase(); + + var iOS = /ipad|iphone|ipod/.test(ua) && !window.MSStream; + var android = /android/.test(ua) && !window.MSStream; + isSlowDeviceResult = (iOS === true || android === true); + } + catch (e) { + isSlowDeviceResult = false; + } + + return isSlowDeviceResult; + }; + // ---------------------------------------------------------------------------------------------------------------- // the defaults for all charts @@ -214,7 +256,7 @@ var NETDATA = window.NETDATA || {}; before: 0, // panning after: -600, // panning pixels_per_point: 1, // the detail of the chart - fill_luminance: 0.8 // luminance of colors in solit areas + fill_luminance: 0.8 // luminance of colors in solid areas }; // ---------------------------------------------------------------------------------------------------------------- @@ -233,12 +275,12 @@ var NETDATA = window.NETDATA || {}; // new elements we have to check. auto_refresher_fast_weight: 0, // this is the current time in ms, spent - // rendering charts continiously. + // rendering charts continuously. // used with .current.fast_render_timeframe page_is_visible: true, // when true, this page is visible - auto_refresher_stop_until: 0, // timestamp in ms - used internaly, to stop the + auto_refresher_stop_until: 0, // timestamp in ms - used internally, to stop the // auto-refresher for some time (when a chart is // performing pan or zoom, we need to stop refreshing // all other charts, to have the maximum speed for @@ -252,7 +294,7 @@ var NETDATA = window.NETDATA || {}; // the current profile // we may have many... current: { - pixels_per_point: 1, // the minimum pixels per point for all charts + pixels_per_point: isSlowDevice()?5:1, // the minimum pixels per point for all charts // increase this to speed javascript up // each chart library has its own limit too // the max of this and the chart library is used @@ -262,7 +304,7 @@ var NETDATA = window.NETDATA || {}; idle_between_charts: 100, // ms - how much time to wait between chart updates - fast_render_timeframe: 200, // ms - render continously until this time of continious + fast_render_timeframe: 200, // ms - render continuously until this time of continuous // rendering has been reached // this setting is used to make it render e.g. 10 // charts at once, sleep idle_between_charts time @@ -276,8 +318,8 @@ var NETDATA = window.NETDATA || {}; idle_lost_focus: 500, // ms - when the window does not have focus, check // if focus has been regained, every this time - global_pan_sync_time: 1000, // ms - when you pan or zoon a chart, the background - // autorefreshing of charts is paused for this amount + global_pan_sync_time: 1000, // ms - when you pan or zoom a chart, the background + // auto-refreshing of charts is paused for this amount // of time sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount @@ -294,11 +336,11 @@ var NETDATA = window.NETDATA || {}; update_only_visible: true, // enable or disable visibility management - parallel_refresher: true, // enable parallel refresh of charts + parallel_refresher: (isSlowDevice() === false), // enable parallel refresh of charts concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts - destroy_on_hide: false, // destroy charts when they are not visible + destroy_on_hide: (isSlowDevice() === true), // destroy charts when they are not visible show_help: netdataShowHelp, // when enabled the charts will show some help show_help_delay_show_ms: 500, @@ -311,7 +353,7 @@ var NETDATA = window.NETDATA || {}; double_click_speed: 500, // ms - time between clicks / taps to detect double click/tap - smooth_plot: true, // enable smooth plot, where possible + smooth_plot: (isSlowDevice() === false), // enable smooth plot, where possible charts_selection_animation_delay: 50, // delay to animate charts when syncing selection @@ -363,6 +405,28 @@ var NETDATA = window.NETDATA || {}; callback: {} // only used for resetting back to defaults }; + NETDATA.localStorageTested = -1; + NETDATA.localStorageTest = function() { + if(NETDATA.localStorageTested !== -1) + return NETDATA.localStorageTested; + + if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + var test = 'test'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + NETDATA.localStorageTested = true; + } + catch (e) { + NETDATA.localStorageTested = false; + } + } + else + NETDATA.localStorageTested = false; + + return NETDATA.localStorageTested; + }; + NETDATA.localStorageGet = function(key, def, callback) { var ret = def; @@ -371,7 +435,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.localStorage.callback[key.toString()] = callback; } - if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + if(NETDATA.localStorageTest() === true) { try { // console.log('localStorage: loading "' + key.toString() + '"'); ret = localStorage.getItem(key.toString()); @@ -413,7 +477,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.localStorage.callback[key.toString()] = callback; } - if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + if(NETDATA.localStorageTest() === true) { // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"'); try { localStorage.setItem(key.toString(), JSON.stringify(value)); @@ -719,7 +783,7 @@ var NETDATA = window.NETDATA || {}; // find the common min var m = min; for(var i in t) - if(t[i] < m) m = t[i]; + if(t.hasOwnProperty(i) && t[i] < m) m = t[i]; //state.log('commonMin ' + state.__commonMin + ' updated: ' + m); this.latest[name] = m; @@ -774,7 +838,7 @@ var NETDATA = window.NETDATA || {}; // find the common max var m = max; for(var i in t) - if(t[i] > m) m = t[i]; + if(t.hasOwnProperty(i) && t[i] > m) m = t[i]; //state.log('commonMax ' + state.__commonMax + ' updated: ' + m); this.latest[name] = m; @@ -873,8 +937,8 @@ var NETDATA = window.NETDATA || {}; // every time a chart is panned or zoomed // we set the timestamp here // then we use it as a sequence number - // to find if other charts are syncronized - // to this timerange + // to find if other charts are synchronized + // to this time-range master: null, // the master chart (state), to which all others // are synchronized @@ -924,14 +988,12 @@ var NETDATA = window.NETDATA || {}; // is the given state the master of the global // pan and zoom sync? isMaster: function(state) { - if(this.master === state) return true; - return false; + return (this.master === state); }, // are we currently have a global pan and zoom sync? isActive: function() { - if(this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0) return true; - return false; + return (this.master !== null && this.force_before_ms !== null && this.force_after_ms !== null && this.seq !== 0); }, // check if a chart, other than the master @@ -943,10 +1005,7 @@ var NETDATA = window.NETDATA || {}; //if(state.needsRecreation()) // return true; - if(state.tm.pan_and_zoom_seq === this.seq) - return false; - - return true; + return (state.tm.pan_and_zoom_seq !== this.seq); } }; @@ -956,18 +1015,14 @@ var NETDATA = window.NETDATA || {}; // FIXME // move color assignment to dimensions, here - dimensionStatus = function(parent, label, name_div, value_div, color) { + var dimensionStatus = function(parent, label, name_div, value_div, color) { this.enabled = false; this.parent = parent; this.label = label; this.name_div = null; this.value_div = null; this.color = NETDATA.themes.current.foreground; - - if(parent.unselected_count === 0) - this.selected = true; - else - this.selected = false; + this.selected = (parent.unselected_count === 0); this.setOptions(name_div, value_div, color); }; @@ -1072,7 +1127,7 @@ var NETDATA = window.NETDATA || {}; // ---------------------------------------------------------------------------------------------------------------- - dimensionsVisibility = function(state) { + var dimensionsVisibility = function(state) { this.state = state; this.len = 0; this.dimensions = {}; @@ -1127,7 +1182,7 @@ var NETDATA = window.NETDATA || {}; }; dimensionsVisibility.prototype.selected2BooleanArray = function(array) { - var ret = new Array(); + var ret = []; this.selected_count = 0; this.unselected_count = 0; @@ -1181,7 +1236,7 @@ var NETDATA = window.NETDATA || {}; // ---------------------------------------------------------------------------------------------------------------- // Our state object, where all per-chart values are stored - chartState = function(element) { + var chartState = function(element) { var self = $(element); this.element = element; @@ -1292,7 +1347,6 @@ var NETDATA = window.NETDATA || {}; this.override_options = self.data('override-options') || null; // override options to pass to netdata this.running = false; // boolean - true when the chart is being refreshed now - this.validated = false; // boolean - has the chart been validated? this.enabled = true; // boolean - is the chart enabled for refresh? this.paused = false; // boolean - is the chart paused for any reason? this.selected = false; // boolean - is the chart shown a selection? @@ -1309,9 +1363,7 @@ var NETDATA = window.NETDATA || {}; this.value_decimal_detail = -1; var d = self.data('decimal-digits'); if(typeof d === 'number') { - this.value_decimal_detail = 1; - while(d-- > 0) - this.value_decimal_detail *= 10; + this.value_decimal_detail = d; } this.auto = { @@ -1363,8 +1415,9 @@ var NETDATA = window.NETDATA || {}; // find the element that needs to be updated var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms - if(refresh_dt_element_name !== null) + if(refresh_dt_element_name !== null) { this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null; + } else this.refresh_dt_element = null; @@ -1555,10 +1608,7 @@ var NETDATA = window.NETDATA || {}; }; var isHidden = function() { - if(typeof that.___chartIsHidden___ !== 'undefined') - return true; - - return false; + return (typeof that.___chartIsHidden___ !== 'undefined'); }; // hide the chart, when it is not visible - called from isVisible() @@ -1610,10 +1660,7 @@ var NETDATA = window.NETDATA || {}; }; var canBeRendered = function() { - if(isHidden() === true || that.isVisible(true) === false) - return false; - - return true; + return (isHidden() === false && that.isVisible(true) === true); }; // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers @@ -1825,6 +1872,8 @@ var NETDATA = window.NETDATA || {}; this.element_legend_childs.resize_handler.onmouseup = this.element_legend_childs.resize_handler.ontouchend = function(e) { + void(e); + // remove all the hooks document.onmouseup = document.onmousemove = @@ -1899,10 +1948,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.current.sync_selection === false) return false; - if(NETDATA.globalSelectionSync.dont_sync_before > Date.now()) - return false; - - return true; + return (NETDATA.globalSelectionSync.dont_sync_before <= Date.now()); }; this.globalSelectionSyncIsMaster = function() { @@ -1937,7 +1983,7 @@ var NETDATA = window.NETDATA || {}; var targets = NETDATA.options.targets; var len = targets.length; while(len--) { - st = targets[len]; + var st = targets[len]; if(st === this) { if(this.debug === true) @@ -1956,14 +2002,11 @@ var NETDATA = window.NETDATA || {}; // can the chart participate to the global selection sync as a slave? this.globalSelectionSyncIsEligible = function() { - if(this.enabled === true + return (this.enabled === true && this.library !== null && typeof this.library.setSelection === 'function' && this.isVisible() === true - && this.chart_created === true) - return true; - - return false; + && this.chart_created === true); }; // this chart becomes a slave of the global selection sync @@ -2022,13 +2065,10 @@ var NETDATA = window.NETDATA || {}; }; this.setSelection = function(t) { - if(typeof this.library.setSelection === 'function') { - if(this.library.setSelection(this, t) === true) - this.selected = true; - else - this.selected = false; - } - else this.selected = true; + if(typeof this.library.setSelection === 'function') + this.selected = (this.library.setSelection(this, t) === true); + else + this.selected = true; if(this.selected === true && this.debug === true) this.log('selection set to ' + t.toString()); @@ -2038,13 +2078,10 @@ var NETDATA = window.NETDATA || {}; this.clearSelection = function() { if(this.selected === true) { - if(typeof this.library.clearSelection === 'function') { - if(this.library.clearSelection(this) === true) - this.selected = false; - else - this.selected = true; - } - else this.selected = false; + if(typeof this.library.clearSelection === 'function') + this.selected = (this.library.clearSelection(this) !== true); + else + this.selected = false; if(this.selected === false && this.debug === true) this.log('selection cleared'); @@ -2057,9 +2094,7 @@ var NETDATA = window.NETDATA || {}; // find if a timestamp (ms) is shown in the current chart this.timeIsVisible = function(t) { - if(t >= this.data_after && t <= this.data_before) - return true; - return false; + return (t >= this.data_after && t <= this.data_before); }; this.calculateRowForTime = function(t) { @@ -2210,19 +2245,69 @@ var NETDATA = window.NETDATA || {}; return ret; }; + var __legendFormatValueChartDecimalsLastMin = undefined; + var __legendFormatValueChartDecimalsLastMax = undefined; + var __legendFormatValueChartDecimals = -1; + this.legendFormatValueDecimalsFromMinMax = function(min, max) { + if(min === __legendFormatValueChartDecimalsLastMin && max === __legendFormatValueChartDecimalsLastMax) + return; + + __legendFormatValueChartDecimalsLastMin = min; + __legendFormatValueChartDecimalsLastMax = max; + + if(this.data !== null && this.data.min === this.data.max) + __legendFormatValueChartDecimals = -1; + + else if(this.value_decimal_detail !== -1) + __legendFormatValueChartDecimals = this.value_decimal_detail; + + else { + var delta; + + if (min === max) + delta = Math.abs(min); + else + delta = Math.abs(max - min); + + if (delta > 1000) __legendFormatValueChartDecimals = 0; + else if (delta > 10) __legendFormatValueChartDecimals = 1; + else if (delta > 1) __legendFormatValueChartDecimals = 2; + else if (delta > 0.1) __legendFormatValueChartDecimals = 2; + else __legendFormatValueChartDecimals = 4; + } + }; + this.legendFormatValue = function(value) { - if(value === null || value === 'undefined') return '-'; - if(typeof value !== 'number') return value; - - if(this.value_decimal_detail !== -1) - return (Math.round(value * this.value_decimal_detail) / this.value_decimal_detail).toLocaleString(); - - var abs = Math.abs(value); - if(abs >= 1000) return (Math.round(value)).toLocaleString(); - if(abs >= 100 ) return (Math.round(value * 10) / 10).toLocaleString(); - if(abs >= 1 ) return (Math.round(value * 100) / 100).toLocaleString(); - if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString(); - return (Math.round(value * 10000) / 10000).toLocaleString(); + if(typeof value !== 'number') return '-'; + + var dmin, dmax; + + if(__legendFormatValueChartDecimals < 0) { + dmin = 0; + var abs = value; + if(abs > 1000) dmax = 0; + else if(abs > 10 ) dmax = 1; + else if(abs > 1) dmax = 2; + else if(abs > 0.1) dmax = 2; + else dmax = 4; + } + else { + dmin = dmax = __legendFormatValueChartDecimals; + } + + if(this.value_decimal_detail !== -1) { + dmin = dmax = this.value_decimal_detail; + } + + return value.toLocaleString(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: dmin, + maximumFractionDigits: dmax + }); }; this.legendSetLabelValue = function(label, value) { @@ -2292,33 +2377,44 @@ var NETDATA = window.NETDATA || {}; } }; + this.legendSetDateLast = { + ms: 0, + date: undefined, + time: undefined + }; + this.legendSetDate = function(ms) { if(typeof ms !== 'number') { this.legendShowUndefined(); return; } - var d = new Date(ms); + if(this.legendSetDateLast.ms !== ms) { + var d = new Date(ms); + this.legendSetDateLast.ms = ms; + this.legendSetDateLast.date = d.toLocaleDateString(); + this.legendSetDateLast.time = d.toLocaleTimeString(); + } - if(this.element_legend_childs.title_date) - this.__legendSetDateString(d.toLocaleDateString()); + if(this.element_legend_childs.title_date !== null) + this.__legendSetDateString(this.legendSetDateLast.date); - if(this.element_legend_childs.title_time) - this.__legendSetTimeString(d.toLocaleTimeString()); + if(this.element_legend_childs.title_time !== null) + this.__legendSetTimeString(this.legendSetDateLast.time); - if(this.element_legend_childs.title_units) + if(this.element_legend_childs.title_units !== null) this.__legendSetUnitsString(this.units) }; this.legendShowUndefined = function() { - if(this.element_legend_childs.title_date) + if(this.element_legend_childs.title_date !== null) this.__legendSetDateString(' '); - if(this.element_legend_childs.title_time) + if(this.element_legend_childs.title_time !== null) this.__legendSetTimeString(this.chart.name); - if(this.element_legend_childs.title_units) - this.__legendSetUnitsString(' ') + if(this.element_legend_childs.title_units !== null) + this.__legendSetUnitsString(' '); if(this.data && this.element_legend_childs.series !== null) { var labels = this.data.dimension_names; @@ -2326,8 +2422,7 @@ var NETDATA = window.NETDATA || {}; while(i--) { var label = labels[i]; - if(typeof label === 'undefined') continue; - if(typeof this.element_legend_childs.series[label] === 'undefined') continue; + if(typeof label === 'undefined' || typeof this.element_legend_childs.series[label] === 'undefined') continue; this.legendSetLabelValue(label, null); } } @@ -2400,8 +2495,8 @@ var NETDATA = window.NETDATA || {}; this.chartColors = function() { if(this.colors !== null) return this.colors; - this.colors = new Array(); - this.colors_available = new Array(); + this.colors = []; + this.colors_available = []; // add the standard colors var len = NETDATA.themes.current.colors.length; @@ -2526,7 +2621,7 @@ var NETDATA = window.NETDATA || {}; + state.chart.chart_type + '" style="background-color: ' + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')' - + '">' + + '">'; var text = document.createTextNode(' ' + name); label.name.appendChild(text); @@ -2739,16 +2834,19 @@ var NETDATA = window.NETDATA || {}; this.element_legend_childs.title_date.className += " netdata-legend-title-date"; this.element_legend.appendChild(this.element_legend_childs.title_date); + this.__last_shown_legend_date = undefined; this.element_legend.appendChild(document.createElement('br')); this.element_legend_childs.title_time.className += " netdata-legend-title-time"; this.element_legend.appendChild(this.element_legend_childs.title_time); + this.__last_shown_legend_time = undefined; this.element_legend.appendChild(document.createElement('br')); this.element_legend_childs.title_units.className += " netdata-legend-title-units"; this.element_legend.appendChild(this.element_legend_childs.title_units); + this.__last_shown_legend_units = undefined; this.element_legend.appendChild(document.createElement('br')); @@ -2767,7 +2865,7 @@ var NETDATA = window.NETDATA || {}; placement: 'bottom', title: 'Chart Legend', delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms }, - content: 'You can click or tap on the values or the labels to select dimentions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.
Help, can be disabled from the settings.' + content: 'You can click or tap on the values or the labels to select dimensions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.
Help, can be disabled from the settings.' }); } else { @@ -2799,7 +2897,7 @@ var NETDATA = window.NETDATA || {}; } } else { - var tmp = new Array(); + var tmp = []; keys = Object.keys(this.chart.dimensions); for(i = 0, len = keys.length; i < len ;i++) { dim = keys[i]; @@ -3004,12 +3102,12 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.current.pan_and_zoom_data_padding === true && this.requested_padding !== null) { if(this.view_after < this.data_after) { - // console.log('adusting view_after from ' + this.view_after + ' to ' + this.data_after); + // console.log('adjusting view_after from ' + this.view_after + ' to ' + this.data_after); this.view_after = this.data_after; } if(this.view_before > this.data_before) { - // console.log('adusting view_before from ' + this.view_before + ' to ' + this.data_before); + // console.log('adjusting view_before from ' + this.view_before + ' to ' + this.data_before); this.view_before = this.data_before; } } @@ -3041,7 +3139,8 @@ var NETDATA = window.NETDATA || {}; if(this.debug === true) this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.'); - this.chart_created = false; + init(); + return; } // check and update the legend @@ -3582,10 +3681,10 @@ var NETDATA = window.NETDATA || {}; // ---------------------------------------------------------------------------------------------------------------- - // this is purely sequencial charts refresher + // this is purely sequential charts refresher // it is meant to be autonomous NETDATA.chartRefresherNoParallel = function(index) { - if(NETDATA.options.debug.mail_loop === true) + if(NETDATA.options.debug.main_loop === true) console.log('NETDATA.chartRefresherNoParallel(' + index + ')'); if(NETDATA.options.updated_dom === true) { @@ -3628,41 +3727,11 @@ var NETDATA = window.NETDATA || {}; } }; - // this is part of the parallel refresher - // its cause is to refresh sequencially all the charts - // that depend on chart library initialization - // it will call the parallel refresher back - // as soon as it sees a chart that its chart library - // is initialized - NETDATA.chartRefresher_uninitialized = function() { - if(NETDATA.options.updated_dom === true) { - // the dom has been updated - // get the dom parts again - NETDATA.parseDom(NETDATA.chartRefresher); - return; - } - - if(NETDATA.options.sequencial.length === 0) - NETDATA.chartRefresher(); - else { - var state = NETDATA.options.sequencial.pop(); - if(state.library.initialized === true) - NETDATA.chartRefresher(); - else - state.autoRefresh(NETDATA.chartRefresher_uninitialized); - } - }; - NETDATA.chartRefresherWaitTime = function() { return NETDATA.options.current.idle_parallel_loops; }; // the default refresher - // it will create 2 sets of charts: - // - the ones that can be refreshed in parallel - // - the ones that depend on something else - // the first set will be executed in parallel - // the second will be given to NETDATA.chartRefresher_uninitialized() NETDATA.chartRefresher = function() { // console.log('auto-refresher...'); @@ -3695,7 +3764,7 @@ var NETDATA = window.NETDATA || {}; return; } - var parallel = new Array(); + var parallel = []; var targets = NETDATA.options.targets; var len = targets.length; var state; @@ -3742,7 +3811,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.main_loop === true) console.log('DOM updated - there are ' + targets.length + ' charts on page.'); - NETDATA.options.targets = new Array(); + NETDATA.options.targets = []; var len = targets.length; while(len--) { // the initialization will take care of sizing @@ -3788,12 +3857,14 @@ var NETDATA = window.NETDATA || {}; $('a[data-toggle="tab"]').on('shown.bs.tab', NETDATA.onscroll); // bootstrap modal switching - $('.modal').on('hidden.bs.modal', NETDATA.onscroll); - $('.modal').on('shown.bs.modal', NETDATA.onscroll); + var $modal = $('.modal'); + $modal.on('hidden.bs.modal', NETDATA.onscroll); + $modal.on('shown.bs.modal', NETDATA.onscroll); // bootstrap collapse switching - $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll); - $('.collapse').on('shown.bs.collapse', NETDATA.onscroll); + var $collapse = $('.collapse'); + $collapse.on('hidden.bs.collapse', NETDATA.onscroll); + $collapse.on('shown.bs.collapse', NETDATA.onscroll); NETDATA.parseDom(NETDATA.chartRefresher); @@ -4053,7 +4124,7 @@ var NETDATA = window.NETDATA || {}; return true; }; - NETDATA.dygraphClearSelection = function(state, t) { + NETDATA.dygraphClearSelection = function(state) { if(typeof state.dygraph_instance !== 'undefined') { state.dygraph_instance.clearSelection(); } @@ -4200,125 +4271,205 @@ var NETDATA = window.NETDATA || {}; var self = $(state.element); - var chart_type = state.chart.chart_type; + var chart_type = self.data('dygraph-type') || state.chart.chart_type; if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area'; - chart_type = self.data('dygraph-type') || chart_type; - - var smooth = (chart_type === 'line' && !NETDATA.chartLibraries.dygraph.isSparkline(state))?true:false; - smooth = self.data('dygraph-smooth') || smooth; - if(NETDATA.dygraph.smooth === false) - smooth = false; + var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state) === true)?3:4; - var strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7) - var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4; + var smooth = (NETDATA.dygraph.smooth === true) + ?(self.data('dygraph-smooth') || (chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false)) + :false; state.dygraph_options = { - colors: self.data('dygraph-colors') || state.chartColors(), + colors: self.data('dygraph-colors') || state.chartColors(), // leave a few pixels empty on the right of the chart - rightGap: self.data('dygraph-rightgap') || 5, - showRangeSelector: self.data('dygraph-showrangeselector') || false, - showRoller: self.data('dygraph-showroller') || false, - - title: self.data('dygraph-title') || state.title, - titleHeight: self.data('dygraph-titleheight') || 19, - - legend: self.data('dygraph-legend') || 'always', // we need this to get selection events - labels: data.result.labels, - labelsDiv: self.data('dygraph-labelsdiv') || state.element_legend_childs.hidden, - labelsDivStyles: self.data('dygraph-labelsdivstyles') || { 'fontSize':'1px' }, - labelsDivWidth: self.data('dygraph-labelsdivwidth') || state.chartWidth() - 70, - labelsSeparateLines: self.data('dygraph-labelsseparatelines') || true, - labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') || true, - labelsKMB: false, - labelsKMG2: false, - showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') || true, - hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') || true, - - includeZero: self.data('dygraph-includezero') || ((chart_type === 'stacked')? true : false), - xRangePad: self.data('dygraph-xrangepad') || 0, - yRangePad: self.data('dygraph-yrangepad') || 1, - - valueRange: self.data('dygraph-valuerange') || [ null, null ], - - ylabel: state.units, - yLabelWidth: self.data('dygraph-ylabelwidth') || 12, - - // the function to plot the chart - plotter: null, - - // The width of the lines connecting data points. This can be used to increase the contrast or some graphs. - strokeWidth: self.data('dygraph-strokewidth') || strokeWidth, - strokePattern: self.data('dygraph-strokepattern') || undefined, - - // The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is "isolated", - // i.e. there is a missing point on either side of it. This also controls the size of those dots. - drawPoints: self.data('dygraph-drawpoints') || false, - - // Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities. - drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') || true, - - connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') || false, - pointSize: self.data('dygraph-pointsize') || 1, - - // enabling this makes the chart with little square lines - stepPlot: self.data('dygraph-stepplot') || false, - - // Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines. - strokeBorderColor: self.data('dygraph-strokebordercolor') || NETDATA.themes.current.background, - strokeBorderWidth: self.data('dygraph-strokeborderwidth') || (chart_type === 'stacked')?0.0:0.0, - - fillGraph: self.data('dygraph-fillgraph') || ((chart_type === 'area' || chart_type === 'stacked')?true:false), - fillAlpha: self.data('dygraph-fillalpha') || ((chart_type === 'stacked')?NETDATA.options.current.color_fill_opacity_stacked:NETDATA.options.current.color_fill_opacity_area), - stackedGraph: self.data('dygraph-stackedgraph') || ((chart_type === 'stacked')?true:false), - stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') || 'none', - - drawAxis: self.data('dygraph-drawaxis') || true, - axisLabelFontSize: self.data('dygraph-axislabelfontsize') || 10, - axisLineColor: self.data('dygraph-axislinecolor') || NETDATA.themes.current.axis, - axisLineWidth: self.data('dygraph-axislinewidth') || 1.0, - - drawGrid: self.data('dygraph-drawgrid') || true, - gridLinePattern: self.data('dygraph-gridlinepattern') || null, - gridLineWidth: self.data('dygraph-gridlinewidth') || 1.0, - gridLineColor: self.data('dygraph-gridlinecolor') || NETDATA.themes.current.grid, - - maxNumberWidth: self.data('dygraph-maxnumberwidth') || 8, - sigFigs: self.data('dygraph-sigfigs') || null, - digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') || 2, - valueFormatter: self.data('dygraph-valueformatter') || function(x){ return x.toFixed(2); }, - - highlightCircleSize: self.data('dygraph-highlightcirclesize') || highlightCircleSize, - highlightSeriesOpts: self.data('dygraph-highlightseriesopts') || null, // TOO SLOW: { strokeWidth: 1.5 }, - highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5, - - pointClickCallback: self.data('dygraph-pointclickcallback') || undefined, - visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names), + rightGap: self.data('dygraph-rightgap') + || 5, + + showRangeSelector: self.data('dygraph-showrangeselector') + || false, + + showRoller: self.data('dygraph-showroller') + || false, + + title: self.data('dygraph-title') + || state.title, + + titleHeight: self.data('dygraph-titleheight') + || 19, + + legend: self.data('dygraph-legend') + || 'always', // we need this to get selection events + + labels: data.result.labels, + + labelsDiv: self.data('dygraph-labelsdiv') + || state.element_legend_childs.hidden, + + labelsDivStyles: self.data('dygraph-labelsdivstyles') + || { 'fontSize':'1px' }, + + labelsDivWidth: self.data('dygraph-labelsdivwidth') + || state.chartWidth() - 70, + + labelsSeparateLines: self.data('dygraph-labelsseparatelines') + || true, + + labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') + || true, + + labelsKMB: false, + labelsKMG2: false, + + showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') + || true, + + hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') + || true, + + includeZero: self.data('dygraph-includezero') + || (chart_type === 'stacked'), + + xRangePad: self.data('dygraph-xrangepad') + || 0, + + yRangePad: self.data('dygraph-yrangepad') + || 1, + + valueRange: self.data('dygraph-valuerange') + || [ null, null ], + + ylabel: state.units, + + yLabelWidth: self.data('dygraph-ylabelwidth') + || 12, + + // the function to plot the chart + plotter: null, + + // The width of the lines connecting data points. + // This can be used to increase the contrast or some graphs. + strokeWidth: self.data('dygraph-strokewidth') + || ((chart_type === 'stacked')?0.1:((smooth === true)?1.5:0.7)), + + strokePattern: self.data('dygraph-strokepattern') + || undefined, + + // The size of the dot to draw on each point in pixels (see drawPoints). + // A dot is always drawn when a point is "isolated", + // i.e. there is a missing point on either side of it. + // This also controls the size of those dots. + drawPoints: self.data('dygraph-drawpoints') + || false, + + // Draw points at the edges of gaps in the data. + // This improves visibility of small data segments or other data irregularities. + drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') + || true, + + connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') + || false, + + pointSize: self.data('dygraph-pointsize') + || 1, + + // enabling this makes the chart with little square lines + stepPlot: self.data('dygraph-stepplot') + || false, + + // Draw a border around graph lines to make crossing lines more easily + // distinguishable. Useful for graphs with many lines. + strokeBorderColor: self.data('dygraph-strokebordercolor') + || NETDATA.themes.current.background, + + strokeBorderWidth: self.data('dygraph-strokeborderwidth') + || (chart_type === 'stacked')?0.0:0.0, + + fillGraph: self.data('dygraph-fillgraph') + || (chart_type === 'area' || chart_type === 'stacked'), + + fillAlpha: self.data('dygraph-fillalpha') + || ((chart_type === 'stacked') + ?NETDATA.options.current.color_fill_opacity_stacked + :NETDATA.options.current.color_fill_opacity_area), + + stackedGraph: self.data('dygraph-stackedgraph') + || (chart_type === 'stacked'), + + stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') + || 'none', + + drawAxis: self.data('dygraph-drawaxis') + || true, + + axisLabelFontSize: self.data('dygraph-axislabelfontsize') + || 10, + + axisLineColor: self.data('dygraph-axislinecolor') + || NETDATA.themes.current.axis, + + axisLineWidth: self.data('dygraph-axislinewidth') + || 1.0, + + drawGrid: self.data('dygraph-drawgrid') + || true, + + gridLinePattern: self.data('dygraph-gridlinepattern') + || null, + + gridLineWidth: self.data('dygraph-gridlinewidth') + || 1.0, + + gridLineColor: self.data('dygraph-gridlinecolor') + || NETDATA.themes.current.grid, + + maxNumberWidth: self.data('dygraph-maxnumberwidth') + || 8, + + sigFigs: self.data('dygraph-sigfigs') + || null, + + digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') + || 2, + + valueFormatter: self.data('dygraph-valueformatter') + || undefined, + + highlightCircleSize: self.data('dygraph-highlightcirclesize') + || highlightCircleSize, + + highlightSeriesOpts: self.data('dygraph-highlightseriesopts') + || null, // TOO SLOW: { strokeWidth: 1.5 }, + + highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') + || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5, + + pointClickCallback: self.data('dygraph-pointclickcallback') + || undefined, + + visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names), + axes: { x: { pixelsPerLabel: 50, ticker: Dygraph.dateTicker, axisLabelFormatter: function (d, gran) { + void(gran); return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds()); - }, - valueFormatter: function (ms) { - //var d = new Date(ms); - //return d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); - - // no need to return anything here - return ' '; - } }, y: { pixelsPerLabel: 15, - valueFormatter: function (x) { - // we format legends with the state object - // no need to do anything here - // return (Math.round(x*100) / 100).toLocaleString(); - // return state.legendFormatValue(x); - return x; + axisLabelFormatter: function (y) { + + // unfortunately, we have to call this every single time + state.legendFormatValueDecimalsFromMinMax( + this.axes_[0].extremeRange[0], + this.axes_[0].extremeRange[1] + ); + + return state.legendFormatValue(y); } } }, @@ -4359,6 +4510,8 @@ var NETDATA = window.NETDATA || {}; } }, zoomCallback: function(minDate, maxDate, yRanges) { + void(yRanges); + if(NETDATA.options.debug.dygraph === true) state.log('dygraphZoomCallback()'); @@ -4372,6 +4525,8 @@ var NETDATA = window.NETDATA || {}; state.updateChartPanOrZoom(minDate, maxDate); }, highlightCallback: function(event, x, points, row, seriesName) { + void(seriesName); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphHighlightCallback()'); @@ -4381,7 +4536,7 @@ var NETDATA = window.NETDATA || {}; // the time it thinks is selected is wrong // here we calculate the time t based on the row number selected // which is ok - var t = state.data_after + row * state.data_update_every; + // var t = state.data_after + row * state.data_update_every; // console.log('row = ' + row + ', x = ' + x + ', t = ' + t + ' ' + ((t === x)?'SAME':(Math.abs(x-t)<=state.data_update_every)?'SIMILAR':'DIFFERENT') + ', rows in db: ' + state.data_points + ' visible(x) = ' + state.timeIsVisible(x) + ' visible(t) = ' + state.timeIsVisible(t) + ' r(x) = ' + state.calculateRowForTime(x) + ' r(t) = ' + state.calculateRowForTime(t) + ' range: ' + state.data_after + ' - ' + state.data_before + ' real: ' + state.data.after + ' - ' + state.data.before + ' every: ' + state.data_update_every); state.globalSelectionSync(x); @@ -4391,6 +4546,8 @@ var NETDATA = window.NETDATA || {}; // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000; }, unhighlightCallback: function(event) { + void(event); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphUnhighlightCallback()'); @@ -4474,17 +4631,26 @@ var NETDATA = window.NETDATA || {}; } }, click: function(event, dygraph, context) { + void(dygraph); + void(context); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.click()'); event.preventDefault(); }, dblclick: function(event, dygraph, context) { + void(event); + void(dygraph); + void(context); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.dblclick()'); NETDATA.resetAllCharts(state); }, wheel: function(event, dygraph, context) { + void(context); + if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.wheel()'); @@ -4496,7 +4662,7 @@ var NETDATA = window.NETDATA || {}; var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0]; var yar0 = g.yAxisRange(0); - // This is calculating the pixel of the higest value. (Top pixel) + // This is calculating the pixel of the highest value. (Top pixel) var yOffset = g.toDomCoords(null, yar0[1])[1]; // x y w and h are relative to the corner of the drawing area, @@ -4607,7 +4773,7 @@ var NETDATA = window.NETDATA || {}; Dygraph.defaultInteractionModel.touchstart(event, dygraph, context); // we overwrite the touch directions at the end, to overwrite - // the internal default of dygraphs + // the internal default of dygraph context.touchDirections = { x: true, y: false }; state.dygraph_last_touch_start = Date.now(); @@ -4636,7 +4802,7 @@ var NETDATA = window.NETDATA || {}; // if it didn't move, it is a selection if(state.dygraph_last_touch_move === 0 && state.dygraph_last_touch_page_x !== 0) { - // internal api of dygraphs + // internal api of dygraph var pct = (state.dygraph_last_touch_page_x - (dygraph.plotter_.area.x + state.element.getBoundingClientRect().left)) / dygraph.plotter_.area.w; var t = Math.round(state.data_after + (state.data_before - state.data_after) * pct); if(NETDATA.dygraphSetSelection(state, t) === true) @@ -4692,7 +4858,7 @@ var NETDATA = window.NETDATA || {}; state.__commonMax = self.data('common-max') || null; } else { - state.log('incompatible version of dygraphs detected'); + state.log('incompatible version of Dygraph detected'); state.__commonMin = null; state.__commonMax = null; } @@ -4978,10 +5144,16 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.d3ChartUpdate = function(state, data) { + void(state); + void(data); + return false; }; NETDATA.d3ChartCreate = function(state, data) { + void(state); + void(data); + return false; }; @@ -5176,10 +5348,11 @@ var NETDATA = window.NETDATA || {}; NETDATA.easypiechartClearSelection = function(state) { if(typeof state.easyPieChartEvent !== 'undefined') { - if(state.easyPieChartEvent.timer !== null) + if(state.easyPieChartEvent.timer !== undefined) { clearTimeout(state.easyPieChartEvent.timer); + } - state.easyPieChartEvent.timer = null; + state.easyPieChartEvent.timer = undefined; } if(state.isAutoRefreshable() === true && state.data !== null) { @@ -5204,7 +5377,7 @@ var NETDATA = window.NETDATA || {}; if(typeof state.easyPieChartEvent === 'undefined') { state.easyPieChartEvent = { - timer: null, + timer: undefined, value: 0, pcent: 0 }; @@ -5219,11 +5392,11 @@ var NETDATA = window.NETDATA || {}; state.easyPieChartEvent.pcent = pcent; state.easyPieChartLabel.innerText = state.legendFormatValue(value); - if(state.easyPieChartEvent.timer === null) { + if(state.easyPieChartEvent.timer === undefined) { state.easyPieChart_instance.disableAnimation(); state.easyPieChartEvent.timer = setTimeout(function() { - state.easyPieChartEvent.timer = null; + state.easyPieChartEvent.timer = undefined; state.easyPieChart_instance.update(state.easyPieChartEvent.pcent); }, NETDATA.options.current.charts_selection_animation_delay); } @@ -5423,9 +5596,10 @@ var NETDATA = window.NETDATA || {}; // is always between min and max var pcent = (value - min) * 100 / (max - min); - // these should never happen - if(pcent < 0) pcent = 0; - if(pcent > 100) pcent = 100; + // bug fix for gauge.js 1.3.1 + // if the value is the absolute min or max, the chart is broken + if(pcent < 0.001) pcent = 0.001; + if(pcent > 99.999) pcent = 99.999; state.gauge_instance.set(pcent); // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max); @@ -5452,10 +5626,11 @@ var NETDATA = window.NETDATA || {}; NETDATA.gaugeClearSelection = function(state) { if(typeof state.gaugeEvent !== 'undefined') { - if(state.gaugeEvent.timer !== null) + if(state.gaugeEvent.timer !== undefined) { clearTimeout(state.gaugeEvent.timer); + } - state.gaugeEvent.timer = null; + state.gaugeEvent.timer = undefined; } if(state.isAutoRefreshable() === true && state.data !== null) { @@ -5481,7 +5656,7 @@ var NETDATA = window.NETDATA || {}; if(typeof state.gaugeEvent === 'undefined') { state.gaugeEvent = { - timer: null, + timer: undefined, value: 0, min: 0, max: 0 @@ -5501,11 +5676,11 @@ var NETDATA = window.NETDATA || {}; state.gaugeEvent.max = max; NETDATA.gaugeSetLabels(state, value, min, max); - if(state.gaugeEvent.timer === null) { + if(state.gaugeEvent.timer === undefined) { NETDATA.gaugeAnimation(state, false); state.gaugeEvent.timer = setTimeout(function() { - state.gaugeEvent.timer = null; + state.gaugeEvent.timer = undefined; NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max); }, NETDATA.options.current.charts_selection_animation_delay); } @@ -5585,19 +5760,22 @@ var NETDATA = window.NETDATA || {}; var options = { lines: 12, // The number of lines to draw - angle: 0.15, // The length of each line - lineWidth: 0.44, // 0.44 The line thickness + angle: 0.15, // The span of the gauge arc + lineWidth: 0.50, // The line thickness + radiusScale: 0.85, // Relative radius pointer: { length: 0.8, // 0.9 The radius of the inner circle strokeWidth: 0.035, // The rotation offset color: pointerColor // Fill color }, + limitMax: true, // If false, the max value of the gauge will be updated if value surpass max + limitMin: true, // If true, the min value of the gauge will be fixed unless you set it manually colorStart: startColor, // Colors colorStop: stopColor, // just experiment with them strokeColor: strokeColor, // to see which ones work best for you - limitMax: true, - generateGradient: (generateGradient === true)?true:false, - gradientType: 0 + generateGradient: (generateGradient === true), + gradientType: 0, + highDpiSupport: true // High resolution support }; if (generateGradient.constructor === Array) { @@ -5607,13 +5785,13 @@ var NETDATA = window.NETDATA || {}; // data-gauge-gradient-percent-color-50="#999900" // data-gauge-gradient-percent-color-100="#000000" - options.percentColors = new Array(); + options.percentColors = []; var len = generateGradient.length; while(len--) { var pcent = generateGradient[len]; - var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false; + var color = self.attr('data-gauge-gradient-percent-color-' + pcent.toString()) || false; if(color !== false) { - var a = new Array(); + var a = []; a[0] = pcent / 100; a[1] = color; options.percentColors.unshift(a); @@ -5623,6 +5801,7 @@ var NETDATA = window.NETDATA || {}; delete options.percentColors; } else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) { + //noinspection PointlessArithmeticExpressionJS options.percentColors = [ [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))], [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))], @@ -5724,31 +5903,21 @@ var NETDATA = window.NETDATA || {}; toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom, initialized: false, enabled: true, - format: function(state) { return 'json'; }, - options: function(state) { return 'ms|flip'; }, + format: function(state) { void(state); return 'json'; }, + options: function(state) { void(state); return 'ms|flip'; }, legend: function(state) { - if(this.isSparkline(state) === false) - return 'right-side'; - else - return null; + return (this.isSparkline(state) === false)?'right-side':null; }, - autoresize: function(state) { return true; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return true; }, + autoresize: function(state) { void(state); return true; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return true; }, pixels_per_point: function(state) { - if(this.isSparkline(state) === false) - return 3; - else - return 2; + return (this.isSparkline(state) === false)?3:2; }, - isSparkline: function(state) { if(typeof state.dygraph_sparkline === 'undefined') { var t = $(state.element).data('dygraph-theme'); - if(t === 'sparkline') - state.dygraph_sparkline = true; - else - state.dygraph_sparkline = false; + state.dygraph_sparkline = (t === 'sparkline'); } return state.dygraph_sparkline; } @@ -5758,126 +5927,126 @@ var NETDATA = window.NETDATA || {}; create: NETDATA.sparklineChartCreate, update: NETDATA.sparklineChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'array'; }, - options: function(state) { return 'flip|abs'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 3; } + format: function(state) { void(state); return 'array'; }, + options: function(state) { void(state); return 'flip|abs'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 3; } }, "peity": { initialize: NETDATA.peityInitialize, create: NETDATA.peityChartCreate, update: NETDATA.peityChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'ssvcomma'; }, - options: function(state) { return 'null2zero|flip|abs'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 3; } + format: function(state) { void(state); return 'ssvcomma'; }, + options: function(state) { void(state); return 'null2zero|flip|abs'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 3; } }, "morris": { initialize: NETDATA.morrisInitialize, create: NETDATA.morrisChartCreate, update: NETDATA.morrisChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'json'; }, - options: function(state) { return 'objectrows|ms'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 50; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 15; } + format: function(state) { void(state); return 'json'; }, + options: function(state) { void(state); return 'objectrows|ms'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 50; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 15; } }, "google": { initialize: NETDATA.googleInitialize, create: NETDATA.googleChartCreate, update: NETDATA.googleChartUpdate, resize: null, - setSelection: undefined, //function(state, t) { return true; }, - clearSelection: undefined, //function(state) { return true; }, + setSelection: undefined, //function(state, t) { void(state); return true; }, + clearSelection: undefined, //function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'datatable'; }, - options: function(state) { return ''; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 300; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 4; } + format: function(state) { void(state); return 'datatable'; }, + options: function(state) { void(state); return ''; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 300; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 4; } }, "raphael": { initialize: NETDATA.raphaelInitialize, create: NETDATA.raphaelChartCreate, update: NETDATA.raphaelChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'json'; }, - options: function(state) { return ''; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 3; } + format: function(state) { void(state); return 'json'; }, + options: function(state) { void(state); return ''; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 3; } }, "c3": { initialize: NETDATA.c3Initialize, create: NETDATA.c3ChartCreate, update: NETDATA.c3ChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'csvjsonarray'; }, - options: function(state) { return 'milliseconds'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 15; } + format: function(state) { void(state); return 'csvjsonarray'; }, + options: function(state) { void(state); return 'milliseconds'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 15; } }, "d3": { initialize: NETDATA.d3Initialize, create: NETDATA.d3ChartCreate, update: NETDATA.d3ChartUpdate, resize: null, - setSelection: undefined, // function(state, t) { return true; }, - clearSelection: undefined, // function(state) { return true; }, + setSelection: undefined, // function(state, t) { void(state); return true; }, + clearSelection: undefined, // function(state) { void(state); return true; }, toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'json'; }, - options: function(state) { return ''; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return false; }, - pixels_per_point: function(state) { return 3; } + format: function(state) { void(state); return 'json'; }, + options: function(state) { void(state); return ''; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return false; }, + pixels_per_point: function(state) { void(state); return 3; } }, "easypiechart": { initialize: NETDATA.easypiechartInitialize, @@ -5889,13 +6058,13 @@ var NETDATA = window.NETDATA || {}; toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'array'; }, - options: function(state) { return 'absolute'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return true; }, - pixels_per_point: function(state) { return 3; }, + format: function(state) { void(state); return 'array'; }, + options: function(state) { void(state); return 'absolute'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return true; }, + pixels_per_point: function(state) { void(state); return 3; }, aspect_ratio: 100 }, "gauge": { @@ -5908,13 +6077,13 @@ var NETDATA = window.NETDATA || {}; toolboxPanAndZoom: null, initialized: false, enabled: true, - format: function(state) { return 'array'; }, - options: function(state) { return 'absolute'; }, - legend: function(state) { return null; }, - autoresize: function(state) { return false; }, - max_updates_to_recreate: function(state) { return 5000; }, - track_colors: function(state) { return true; }, - pixels_per_point: function(state) { return 3; }, + format: function(state) { void(state); return 'array'; }, + options: function(state) { void(state); return 'absolute'; }, + legend: function(state) { void(state); return null; }, + autoresize: function(state) { void(state); return false; }, + max_updates_to_recreate: function(state) { void(state); return 5000; }, + track_colors: function(state) { void(state); return true; }, + pixels_per_point: function(state) { void(state); return 3; }, aspect_ratio: 70 } }; @@ -5940,10 +6109,7 @@ var NETDATA = window.NETDATA || {}; if(typeof $().emulateTransitionEnd === 'function') return true; else { - if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap) - return true; - else - return false; + return (typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap === true); } } }, @@ -5957,10 +6123,7 @@ var NETDATA = window.NETDATA || {}; { url: NETDATA.themes.current.bootstrap_css, isAlreadyLoaded: function() { - if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap) - return true; - else - return false; + return (typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap === true); } }, { @@ -6012,7 +6175,7 @@ var NETDATA = window.NETDATA || {}; if(async === false) NETDATA.loadRequiredJs(++index, callback); - }) + }); if(async === true) NETDATA.loadRequiredJs(++index, callback); @@ -6052,7 +6215,7 @@ var NETDATA = window.NETDATA || {}; last_notification_id: 0, // the id of the last alarm_log we have raised an alarm for first_notification_id: 0, // the id of the first alarm_log entry for this session // this is used to prevent CLEAR notifications for past events - // notifications_shown: new Array(), + // notifications_shown: [], server: null, // the server to connect to for fetching alarms current: null, // the list of raised alarms - updated in the background @@ -6066,16 +6229,18 @@ var NETDATA = window.NETDATA || {}; return; } - var value = entry.value; + var value_string = entry.value_string; + if(NETDATA.alarms.current !== null) { + // get the current value_string var t = NETDATA.alarms.current.alarms[entry.chart + '.' + entry.name]; - if(typeof t !== 'undefined' && entry.status === t.status) - value = t.value; + if(typeof t !== 'undefined' && entry.status === t.status && typeof t.value_string !== 'undefined') + value_string = t.value_string; } var name = entry.name.replace(/_/g, ' '); var status = entry.status.toLowerCase(); - var title = name + ' = ' + ((value === null)?'NaN':Math.floor(value)).toString() + ' ' + entry.units; + var title = name + ' = ' + value_string.toString(); var tag = entry.alarm_id; var icon = 'images/seo-performance-128.png'; var interaction = false; @@ -6104,8 +6269,12 @@ var NETDATA = window.NETDATA || {}; // console.log('alarm' + entry.unique_id + ' switch to CLEAR from ' + entry.old_status); return; } - title = name + ' back to normal'; - icon = 'images/check-mark-2-128-green.png' + if(entry.no_clear_notification === true) { + // console.log('alarm' + entry.unique_id + ' is CLEAR but has no_clear_notification flag'); + return; + } + title = name + ' back to normal (' + value_string.toString() + ')'; + icon = 'images/check-mark-2-128-green.png'; interaction = false; break; @@ -6121,7 +6290,7 @@ var NETDATA = window.NETDATA || {}; if(entry.old_status === 'WARNING') status = 'escalated to ' + entry.status.toLowerCase(); - icon = 'images/alert-128-red.png' + icon = 'images/alert-128-red.png'; interaction = true; break; @@ -6363,12 +6532,12 @@ var NETDATA = window.NETDATA || {}; // Registry of netdata hosts NETDATA.registry = { - server: null, // the netdata registry server - person_guid: null, // the unique ID of this browser / user - machine_guid: null, // the unique ID the netdata server that served dashboard.js - hostname: null, // the hostname of the netdata server that served dashboard.js - machines: null, // the user's other URLs - machines_array: null, // the user's other URLs in an array + server: null, // the netdata registry server + person_guid: null, // the unique ID of this browser / user + machine_guid: null, // the unique ID the netdata server that served dashboard.js + hostname: 'unknown', // the hostname of the netdata server that served dashboard.js + machines: null, // the user's other URLs + machines_array: null, // the user's other URLs in an array person_urls: null, parsePersonUrls: function(person_urls) { @@ -6377,9 +6546,8 @@ var NETDATA = window.NETDATA || {}; if(person_urls) { NETDATA.registry.machines = {}; - NETDATA.registry.machines_array = new Array(); + NETDATA.registry.machines_array = []; - var now = Date.now(); var apu = person_urls; var i = apu.length; while(i--) { @@ -6392,7 +6560,7 @@ var NETDATA = window.NETDATA || {}; last_t: apu[i][2], accesses: apu[i][3], name: apu[i][4], - alternate_urls: new Array() + alternate_urls: [] }; obj.alternate_urls.push(apu[i][1]); diff --git a/web/dashboard_info.js b/web/dashboard_info.js index 24a579cfe..c348da30d 100644 --- a/web/dashboard_info.js +++ b/web/dashboard_info.js @@ -1,7 +1,9 @@ var netdataDashboard = window.netdataDashboard || {}; -// menu +// ---------------------------------------------------------------------------- +// menus + // information about the main menus netdataDashboard.menu = { @@ -12,179 +14,185 @@ netdataDashboard.menu = { }, 'services': { - title: 'Systemd Services', + title: 'systemd Services', icon: '', - info: 'Resources utilization of systemd services.' + info: 'Resources utilization of systemd services. netdata monitors all systemd services via cgroups (the resources accounting used by containers). ' }, 'ap': { title: 'Access Points', icon: '', - info: undefined + info: 'Performance metrics for the access points (i.e. wireless interfaces in AP mode) found on the system.' }, 'tc': { title: 'Quality of Service', icon: '', - info: 'Netdata collects and visualizes tc class utilization using its tc-helper plugin. If you also use FireQOS for setting up QoS, netdata automatically collects interface and class names. If your QoS configuration includes overheads calculation, the values shown here will include these overheads (the total bandwidth for the same interface as reported in the Network Interfaces section, will be lower than the total bandwidth reported here). Also, data collection may have a slight time difference compared to the interface (QoS data collection is implemented with a BASH script, so a shift in data collection of a few milliseconds should be justified).' + info: 'Netdata collects and visualizes tc class utilization using its tc-helper plugin. If you also use FireQOS for setting up QoS, netdata automatically collects interface and class names. If your QoS configuration includes overheads calculation, the values shown here will include these overheads (the total bandwidth for the same interface as reported in the Network Interfaces section, will be lower than the total bandwidth reported here). QoS data collection may have a slight time difference compared to the interface (QoS data collection uses a BASH script, so a shift in data collection of a few milliseconds should be justified).' }, 'net': { title: 'Network Interfaces', icon: '', - info: 'Per network interface statistics collected from /proc/net/dev.' + info: 'Performance metrics for network interfaces.' }, 'ipv4': { title: 'IPv4 Networking', icon: '', - info: undefined + info: 'Metrics for the IPv4 stack of the system. Internet Protocol version 4 (IPv4) is the fourth version of the Internet Protocol (IP). It is one of the core protocols of standards-based internetworking methods in the Internet. IPv4 is a connectionless protocol for use on packet-switched networks. It operates on a best effort delivery model, in that it does not guarantee delivery, nor does it assure proper sequencing or avoidance of duplicate delivery. These aspects, including data integrity, are addressed by an upper layer transport protocol, such as the Transmission Control Protocol (TCP).' }, 'ipv6': { title: 'IPv6 Networking', icon: '', - info: undefined + info: 'Metrics for the IPv6 stack of the system. Internet Protocol version 6 (IPv6) is the most recent version of the Internet Protocol (IP), the communications protocol that provides an identification and location system for computers on networks and routes traffic across the Internet. IPv6 was developed by the Internet Engineering Task Force (IETF) to deal with the long-anticipated problem of IPv4 address exhaustion. IPv6 is intended to replace IPv4.' }, 'ipvs': { title: 'IP Virtual Server', icon: '', - info: undefined + info: 'IPVS (IP Virtual Server) implements transport-layer load balancing inside the Linux kernel, so called Layer-4 switching. IPVS running on a host acts as a load balancer at the front of a cluster of real servers, it can direct requests for TCP/UDP based services to the real servers, and makes services of the real servers to appear as a virtual service on a single IP address.' }, 'netfilter': { title: 'Firewall (netfilter)', icon: '', - info: undefined + info: 'Performance metrics of the netfilter components.' }, 'cpu': { title: 'CPUs', icon: '', - info: undefined + info: 'Detailed information for each CPU of the system. A summary of the system for all CPUs can be found at the System Overview section.' }, 'mem': { title: 'Memory', icon: '', - info: undefined + info: 'Detailed information about the memory management of the system.' }, 'disk': { title: 'Disks', icon: '', - info: 'Charts with performance information for all the system disks. Special care has been given to present disk performance metrics in a way compatible with iostat -x. netdata by default prevents rendering performance charts for individual partitions and unmounted virtual disks. Disabled charts can still be enabled by altering the relative settings in the netdata configuration file.' + info: 'Charts with performance information for all the system disks. Special care has been given to present disk performance metrics in a way compatible with iostat -x. netdata by default prevents rendering performance charts for individual partitions and unmounted virtual disks. Disabled charts can still be enabled by configuring the relative settings in the netdata configuration file.' }, 'sensors': { title: 'Sensors', icon: '', - info: undefined + info: 'Readings of the configured system sensors.' + }, + + 'ipmi': { + title: 'IPMI', + icon: '', + info: 'The Intelligent Platform Management Interface (IPMI) is a set of computer interface specifications for an autonomous computer subsystem that provides management and monitoring capabilities independently of the host system\'s CPU, firmware (BIOS or UEFI) and operating system.' }, 'nfsd': { title: 'NFS Server', icon: '', - info: undefined + info: 'Performance metrics of the Network File Server. NFS is a distributed file system protocol, allowing a user on a client computer to access files over a network, much like local storage is accessed. NFS, like many other protocols, builds on the Open Network Computing Remote Procedure Call (ONC RPC) system. The NFS is an open standard defined in Request for Comments (RFC).' }, 'nfs': { title: 'NFS Client', icon: '', - info: undefined + info: 'Performance metrics of the NFS operations of this system, acting as an NFS client.' }, 'apps': { title: 'Applications', icon: '', - info: 'Per application statistics are collected using netdata\'s apps.plugin. This plugin walks through the entire /proc filesystem and aggregates statistics for applications of interest, defined in /etc/netdata/apps_groups.conf (the default is here). The plugin internally builds a process tree (much like ps fax does), and groups processes together (evaluating both child and parent processes) so that the result is always a chart with a predefined set of dimensions (of course, only application groups found running are reported). The reported values are compatible with top, although the netdata plugin counts also the resources of exited children (unlike top which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', + info: 'Per application statistics are collected using netdata\'s apps.plugin. This plugin walks through all processes and aggregates statistics for applications of interest, defined in /etc/netdata/apps_groups.conf (the default is here). The plugin internally builds a process tree (much like ps fax does), and groups processes together (evaluating both child and parent processes) so that the result is always a chart with a predefined set of dimensions (of course, only application groups found running are reported). The reported values are compatible with top, although the netdata plugin counts also the resources of exited children (unlike top which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', height: 1.5 }, 'users': { title: 'Users', icon: '', - info: 'Per user statistics are collected using netdata\'s apps.plugin. This plugin walks through the entire /proc filesystem and aggregates statistics per user. The reported values are compatible with top, although the netdata plugin counts also the resources of exited children (unlike top which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', + info: 'Per user statistics are collected using netdata\'s apps.plugin. This plugin walks through all processes and aggregates statistics per user. The reported values are compatible with top, although the netdata plugin counts also the resources of exited children (unlike top which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', height: 1.5 }, 'groups': { title: 'User Groups', icon: '', - info: 'Per user group statistics are collected using netdata\'s apps.plugin. This plugin walks through the entire /proc filesystem and aggregates statistics per user group. The reported values are compatible with top, although the netdata plugin counts also the resources of exited children (unlike top which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', + info: 'Per user group statistics are collected using netdata\'s apps.plugin. This plugin walks through all processes and aggregates statistics per user group. The reported values are compatible with top, although the netdata plugin counts also the resources of exited children (unlike top which shows only the resources of the currently running processes). So for processes like shell scripts, the reported values include the resources used by the commands these scripts run within each timeframe.', height: 1.5 }, 'netdata': { title: 'Netdata Monitoring', icon: '', - info: undefined + info: 'Performance metrics for the operation of netdata itself and its plugins.' }, 'example': { title: 'Example Charts', - info: undefined + info: 'Example charts, demonstrating the external plugin architecture.' }, 'cgroup': { title: '', icon: '', - info: undefined + info: 'Container resource utilization metrics. Netdata reads this information from cgroups (abbreviated from control groups), a Linux kernel feature that limits and accounts resource usage (CPU, memory, disk I/O, network, etc.) of a collection of processes. cgroups together with namespaces (that offer isolation between processes) provide what we usually call: containers.' }, 'cgqemu': { title: '', icon: '', - info: undefined + info: 'QEMU virtual machine resource utilization metrics. QEMU (short for Quick Emulator) is a free and open-source hosted hypervisor that performs hardware virtualization.' }, 'fping': { title: 'fping', icon: '', - info: undefined + info: 'Network latency statistics, via fping. fping is a program to send ICMP echo probes to network hosts, similar to ping, but much better performing when pinging multiple hosts. fping versions after 3.15 can be directly used as netdata plugins.' }, 'memcached': { title: 'memcached', icon: '', - info: undefined + info: 'Performance metrics for memcached. Memcached is a general-purpose distributed memory caching system. It is often used to speed up dynamic database-driven websites by caching data and objects in RAM to reduce the number of times an external data source (such as a database or API) must be read.' }, 'mysql': { title: 'MySQL', icon: '', - info: undefined + info: 'Performance metrics for mysql, the open-source relational database management system (RDBMS).' }, 'postgres': { title: 'Postgres', icon: '', - info: undefined + info: 'Performance metrics for PostgresSQL, the object-relational database (ORDBMS).' }, 'redis': { title: 'Redis', icon: '', - info: undefined + info: 'Performance metrics for redis. Redis (REmote DIctionary Server) is a software project that implements data structure servers. It is open-source, networked, in-memory, and stores keys with optional durability.' }, 'retroshare': { title: 'RetroShare', icon: '', - info: undefined + info: 'Performance metrics for RetroShare. RetroShare is open source software for encrypted filesharing, serverless email, instant messaging, online chat, and BBS, based on a friend-to-friend network built on GNU Privacy Guard (GPG).' }, 'ipfs': { title: 'IPFS', icon: '', - info: undefined + info: 'Performance metrics for the InterPlanetary File System (IPFS), a content-addressable, peer-to-peer hypermedia distribution protocol.' }, 'phpfpm': { title: 'PHP-FPM', icon: '', - info: undefined + info: 'Performance metrics for PHP-FPM, an alternative FastCGI implementation for PHP.' }, 'postfix': { @@ -217,6 +225,12 @@ netdataDashboard.menu = { info: undefined }, + 'web_log': { + title: undefined, + icon: '', + info: 'Information extracted from a web server log file. web_log plugin incrementally parses the web server log file to provide, in real-time, a break down of key web server performance metrics. An extended log file format may optionally be used (for nginx and apache) offering timing information and bandwidth for both requests and responses. web_log plugin may also be configured to provide a break down of requests per URL pattern (check /etc/netdata/python.d/web_log.conf).' + }, + 'named': { title: 'named', icon: '', @@ -254,9 +268,31 @@ netdataDashboard.menu = { } }; -// submenu + + +// ---------------------------------------------------------------------------- +// submenus + +// information to be shown, just below each submenu + // information about the submenus netdataDashboard.submenu = { + 'web_log.bandwidth': { + info: 'Bandwidth of requests (received) and responses (sent). received requires an extended log format (without it, the web server log does not have this information). This chart may present unusual spikes, since the bandwidth is accounted at the time the log line is saved by the web server, even if the time needed to serve it spans across a longer duration. We suggest to use QoS (e.g. FireQOS) for accurate accounting of the web server bandwidth.' + }, + + 'web_log.urls': { + info: 'Number of requests for each URL pattern defined in /etc/netdata/python.d/web_log.conf. This chart counts all requests matching the URL patterns defined, independently of the web server response codes (i.e. both successful and unsuccessful).' + }, + + 'web_log.clients': { + info: 'Charts showing the number of unique client IPs, accessing the web server.' + }, + + 'web_log.timings': { + info: 'Web server response timings - the time the web server needed to prepare and respond to requests. This requires an extended log format and its meaning is web server specific. For most web servers this accounts the time from the reception of a complete request, to the dispatch of the last byte of the response. So, it includes the network delays of responses, but it does not include the network delays of requests.' + }, + 'mem.ksm': { title: 'Memory Deduper', info: 'Kernel Same-page Merging (KSM) performance monitoring, read from several files in /sys/kernel/mm/ksm/. KSM is a memory-saving de-duplication feature in the Linux kernel (since version 2.6.32). The KSM daemon ksmd periodically scans those areas of user memory which have been registered with it, looking for pages of identical content which can be replaced by a single write-protected page (which is automatically copied if a process later wants to update its content). KSM was originally developed for use with KVM (where it was known as Kernel Shared Memory), to fit more virtual machines into physical memory, by sharing the data common between them. But it can be useful to any application which generates many instances of the same data.' @@ -272,7 +308,7 @@ netdataDashboard.submenu = { 'netfilter.conntrack': { title: 'Connection Tracker', - info: 'Netfilter Connection Tracker performance monitoring, read from /proc/net/stat/nf_conntrack. The connection tracker keeps track of all connections of the machine, inbound and outbound. It works by keeping a database with all open connections, tracking network and address translation and connection expectations.' + info: 'Netfilter Connection Tracker performance metrics. The connection tracker keeps track of all connections of the machine, inbound and outbound. It works by keeping a database with all open connections, tracking network and address translation and connection expectations.' }, 'netfilter.nfacct': { @@ -282,21 +318,35 @@ netdataDashboard.submenu = { 'netfilter.synproxy': { title: 'DDoS Protection', - info: 'DDoS Protection performance monitoring read from /proc/net/stat/synproxy. SYNPROXY is a TCP SYN packets proxy. It is used to protect any TCP server (like a web server) from SYN floods and similar DDoS attacks. It is a netfilter module, in the Linux kernel (since version 3.12). It is optimized to handle millions of packets per second utilizing all CPUs available without any concurrency locking between the connections. It can be used for any kind of TCP traffic (even encrypted), since it does not interfere with the content itself.' + info: 'DDoS protection performance metrics. SYNPROXY is a TCP SYN packets proxy. It is used to protect any TCP server (like a web server) from SYN floods and similar DDoS attacks. It is a netfilter module, in the Linux kernel (since version 3.12). It is optimized to handle millions of packets per second utilizing all CPUs available without any concurrency locking between the connections. It can be used for any kind of TCP traffic (even encrypted), since it does not interfere with the content itself.' }, 'system.softnet_stat': { title: 'softnet', - info: 'Statistics for CPUs SoftIRQs related to network receive work, read from /proc/net/softnet_stat. Break down per CPU core can be found at CPU / softnet statistics. processed states the number of packets processed, dropped is the number packets dropped because the network device backlog was full (to fix them use sysctl to increase net.core.netdev_max_backlog), squeezed is the number of packets dropped because the network device budget ran out (to fix them use sysctl to increase net.core.netdev_budget). More information about identifying and troubleshooting network driver related issues can be found at Red Hat Enterprise Linux Network Performance Tuning Guide.' + info: function(os) { + if(os === 'linux') + return 'Statistics for CPUs SoftIRQs related to network receive work. Break down per CPU core can be found at CPU / softnet statistics. processed states the number of packets processed, dropped is the number packets dropped because the network device backlog was full (to fix them on Linux use sysctl to increase net.core.netdev_max_backlog), squeezed is the number of packets dropped because the network device budget ran out (to fix them on Linux use sysctl to increase net.core.netdev_budget). More information about identifying and troubleshooting network driver related issues can be found at Red Hat Enterprise Linux Network Performance Tuning Guide.'; + else + return 'Statistics for CPUs SoftIRQs related to network receive work.'; + } }, 'cpu.softnet_stat': { title: 'softnet', - info: 'Statistics for per CPUs core SoftIRQs related to network receive work, read from /proc/net/softnet_stat. Total for all CPU cores can be found at System / softnet statistics. processed states the number of packets processed, dropped is the number packets dropped because the network device backlog was full (to fix them use sysctl to increase net.core.netdev_max_backlog), squeezed is the number of packets dropped because the network device budget ran out (to fix them use sysctl to increase net.core.netdev_budget). More information about identifying and troubleshooting network driver related issues can be found at Red Hat Enterprise Linux Network Performance Tuning Guide.' + info: function(os) { + if(os === 'linux') + return 'Statistics for per CPUs core SoftIRQs related to network receive work. Total for all CPU cores can be found at System / softnet statistics. processed states the number of packets processed, dropped is the number packets dropped because the network device backlog was full (to fix them on Linux use sysctl to increase net.core.netdev_max_backlog), squeezed is the number of packets dropped because the network device budget ran out (to fix them on Linux use sysctl to increase net.core.netdev_budget). More information about identifying and troubleshooting network driver related issues can be found at Red Hat Enterprise Linux Network Performance Tuning Guide.'; + else + return 'Statistics for per CPUs core SoftIRQs related to network receive work. Total for all CPU cores can be found at System / softnet statistics.'; + } } }; + + +// ---------------------------------------------------------------------------- // chart + // information works on the context of a chart // Its purpose is to set: // @@ -308,60 +358,65 @@ netdataDashboard.submenu = { // netdataDashboard.context = { 'system.cpu': { - info: 'Total CPU utilization (all cores). 100% here means there is no CPU idle time at all. You can get per core usage at the CPUs section and per application usage at the Applications Monitoring section.
Keep an eye on iowait ' + sparkline('system.cpu', 'iowait', '%') + '. If it is constantly high, your disks are a bottleneck and they slow your system down.
Another important metric worth monitoring, is softirq ' + sparkline('system.cpu', 'softirq', '%') + '. A constantly high percentage of softirq may indicate network driver issues.', + info: function(os) { + void(os); + return 'Total CPU utilization (all cores). 100% here means there is no CPU idle time at all. You can get per core usage at the CPUs section and per application usage at the Applications Monitoring section.' + + netdataDashboard.sparkline('
Keep an eye on iowait ', 'system.cpu', 'iowait', '%', '. If it is constantly high, your disks are a bottleneck and they slow your system down.') + + netdataDashboard.sparkline('
An important metric worth monitoring, is softirq ', 'system.cpu', 'softirq', '%', '. A constantly high percentage of softirq may indicate network driver issues.'); + }, valueRange: "[0, 100]" }, 'system.load': { - info: 'Current system load, i.e. the number of processes using CPU or waiting for system resources (usually CPU and disk). The 3 metrics refer to 1, 5 and 15 minute averages. Linux calculates this once every 5 seconds. Netdata reads them from /proc/loadavg. For more information check this wikipedia article', + info: 'Current system load, i.e. the number of processes using CPU or waiting for system resources (usually CPU and disk). The 3 metrics refer to 1, 5 and 15 minute averages. The system calculates this once every 5 seconds. For more information check this wikipedia article', height: 0.7 }, 'system.io': { - info: 'Total Disk I/O, for all disks, read from /proc/vmstat. You can get detailed information about each disk at the Disks section and per application Disk usage at the Applications Monitoring section.' + info: 'Total Disk I/O, for all disks. You can get detailed information about each disk at the Disks section and per application Disk usage at the Applications Monitoring section.' }, 'system.swapio': { - info: 'Total Swap I/O, read from /proc/vmstat. (netdata measures both in and out. If either of them is not shown in the chart, it is because it is zero - you can change the page settings to always render all the available dimensions on all charts).' + info: 'Total Swap I/O. (netdata measures both in and out. If either of them is not shown in the chart, it is because it is zero - you can change the page settings to always render all the available dimensions on all charts).' }, 'system.pgfaults': { - info: 'Total page faults, read from /proc/vmstat. Major page faults indicates that the system is using its swap. You can find which applications use the swap at the Applications Monitoring section.' + info: 'Total page faults. Major page faults indicates that the system is using its swap. You can find which applications use the swap at the Applications Monitoring section.' }, 'system.entropy': { colors: '#CC22AA', - info: 'Entropy, read from /proc/sys/kernel/random/entropy_avail, is like a pool of random numbers (/dev/random) that are mainly used in cryptography. It is advised that the pool remains always above 200. If the pool of entropy gets empty, you risk your security to be predictable and you should install a user-space random numbers generating daemon, like haveged or rng-tools (i.e. rngd), to keep the pool in healthy levels.' + info: 'Entropy, is like a pool of random numbers (/dev/random) that are mainly used in cryptography. It is advised that the pool remains always above 200. If the pool of entropy gets empty, you risk your security to be predictable and you should install a user-space random numbers generating daemon, like haveged or rng-tools (i.e. rngd), to keep the pool in healthy levels.' }, 'system.forks': { colors: '#5555DD', - info: 'The number of new processes created per second, read from /proc/stat.' + info: 'Number of new processes created.' }, 'system.intr': { colors: '#DD5555', - info: 'Total number of CPU interrupts, read from /proc/stat. Check system.interrupts that gives more detail about each interrupt and also the CPUs section where interrupts are analyzed per CPU core.' + info: 'Total number of CPU interrupts. Check system.interrupts that gives more detail about each interrupt and also the CPUs section where interrupts are analyzed per CPU core.' }, 'system.interrupts': { - info: 'CPU interrupts in detail, read from /proc/interrupts. At the CPUs section, interrupts are analyzed per CPU core.' + info: 'CPU interrupts in detail. At the CPUs section, interrupts are analyzed per CPU core.' }, 'system.softirqs': { - info: 'CPU softirqs in detail, read from /proc/softirqs. At the CPUs section, softirqs are analyzed per CPU core.' + info: 'CPU softirqs in detail. At the CPUs section, softirqs are analyzed per CPU core.' }, 'system.processes': { - info: 'System processes, read from /proc/stat. Running are the processes in the CPU. Blocked are processes that are willing to enter the CPU, but they cannot, e.g. because they wait for disk activity.' + info: 'System processes. Running are the processes in the CPU. Blocked are processes that are willing to enter the CPU, but they cannot, e.g. because they wait for disk activity.' }, 'system.active_processes': { - info: 'All system processes, read from /proc/loadavg.' + info: 'All system processes.' }, 'system.ctxt': { - info: 'Context Switches, read from /proc/stat, is the switching of the CPU from one process, task or thread to another. If there are many processes or threads willing to execute and very few CPU cores available to handle them, the system is making more context switching to balance the CPU resources among them. The whole process is computationally intensive. The more the context switches, the slower the system gets.' + info: 'Context Switches, is the switching of the CPU from one process, task or thread to another. If there are many processes or threads willing to execute and very few CPU cores available to handle them, the system is making more context switching to balance the CPU resources among them. The whole process is computationally intensive. The more the context switches, the slower the system gets.' }, 'system.idlejitter': { @@ -370,19 +425,19 @@ netdataDashboard.context = { }, 'system.ipv4': { - info: 'Total IPv4 Traffic, read from /proc/net/netstat.' + info: 'Total IPv4 Traffic.' }, 'system.ipv6': { - info: 'Total IPv6 Traffic, read from /proc/net/snmp6.' + info: 'Total IPv6 Traffic.' }, 'system.ram': { - info: 'System memory, read from /proc/meminfo.' + info: 'System Random Access Memory (i.e. physical memory) usage.' }, 'system.swap': { - info: 'System swap memory, read from /proc/meminfo.' + info: 'System swap memory usage. Swap space is used when the amount of physical memory (RAM) is full. When the system needs more memory resources and the RAM is full, inactive pages in memory are moved to the swap space (usually a disk, a disk partition or a file).' }, // ------------------------------------------------------------------------ @@ -396,7 +451,8 @@ netdataDashboard.context = { 'mem.ksm_ratios': { heads: [ - function(id) { + function(os, id) { + void(os); return '
page fault is a type of interrupt, called trap, raised by computer hardware when a running program accesses a memory page that is mapped into the virtual address space, but not actually loaded into main memory. If the page is loaded in memory at the time the fault is generated, but is not marked in the memory management unit as being loaded in memory, then it is called a minor or soft page fault. A major page fault is generated when the system needs to load the memory page from disk or swap memory. These values are read from /proc/vmstat.' + info: 'A page fault is a type of interrupt, called trap, raised by computer hardware when a running program accesses a memory page that is mapped into the virtual address space, but not actually loaded into main memory. If the page is loaded in memory at the time the fault is generated, but is not marked in the memory management unit as being loaded in memory, then it is called a minor or soft page fault. A major page fault is generated when the system needs to load the memory page from disk or swap memory.' }, 'mem.committed': { - info: 'Committed Memory, read from /proc/meminfo, is the sum of all memory which has been allocated by processes.' + colors: NETDATA.colors[3], + info: 'Committed Memory, is the sum of all memory which has been allocated by processes.' }, 'mem.writeback': { - info: 'Read from /proc/meminfo, Dirty is the amount of memory waiting to be written to disk. Writeback is how much memory is actively being written to disk.' + info: 'Dirty is the amount of memory waiting to be written to disk. Writeback is how much memory is actively being written to disk.' }, 'mem.kernel': { - info: 'Read from /proc/meminfo, This chart displays the total ammount of memory being used by the kernel. Slab is the amount of memory used by the kernel to cache data structures for its own use. KernelStack is the amount of memory allocated for each task done by the kernel. PageTables is the amount of memory decicated to the lowest level of page tables (A page table is used to turn a virtual address into a physical memory address). VmallocUsed is the amount of memory being used as virtual address space.' + info: 'The total ammount of memory being used by the kernel. Slab is the amount of memory used by the kernel to cache data structures for its own use. KernelStack is the amount of memory allocated for each task done by the kernel. PageTables is the amount of memory decicated to the lowest level of page tables (A page table is used to turn a virtual address into a physical memory address). VmallocUsed is the amount of memory being used as virtual address space.' }, 'mem.slab': { - info: 'Read from /proc/meminfo, reclaimable is the amount of memory which the kernel can reuse. unreclaimable can not be reused even when the kernel is lacking memory.' + info: 'Reclaimable is the amount of memory which the kernel can reuse. Unreclaimable can not be reused even when the kernel is lacking memory.' }, // ------------------------------------------------------------------------ @@ -528,7 +581,9 @@ netdataDashboard.context = { 'tc.qos': { heads: [ - function(id) { + function(os, id) { + void(os); + if(id.match(/.*-ifb$/)) return netdataDashboard.gaugeChart('Inbound', '12%', '', '#5555AA'); else @@ -571,12 +626,12 @@ netdataDashboard.context = { heads: [ netdataDashboard.gaugeChart('Utilization', '12%', '', '#FF5588') ], - info: 'Disk Utilization measures the amount of time the disk was busy with something. This is not related to its performance. 100% means that the Linux kernel always had an outstanding operation on the disk. Keep in mind that depending on the underlying technology of the disk, 100% here may or may not be an indication of congestion.' + info: 'Disk Utilization measures the amount of time the disk was busy with something. This is not related to its performance. 100% means that the system always had an outstanding operation on the disk. Keep in mind that depending on the underlying technology of the disk, 100% here may or may not be an indication of congestion.' }, 'disk.backlog': { colors: '#0099CC', - info: 'Backlog is an indication of the duration of pending disk operations. On every I/O event the Linux kernel is multiplying the time spent doing I/O since the last update of this field with the number of pending operations. While not accurate, this metric can provide an indication of the expected completion time of the operations in progress.' + info: 'Backlog is an indication of the duration of pending disk operations. On every I/O event the system is multiplying the time spent doing I/O since the last update of this field with the number of pending operations. While not accurate, this metric can provide an indication of the expected completion time of the operations in progress.' }, 'disk.io': { @@ -588,7 +643,7 @@ netdataDashboard.context = { }, 'disk.ops': { - info: 'Completed disk I/O operations. Keep in mind the number of operations requested might be higher, since the Linux kernel is able to merge adjacent to each other (see merged operations chart).' + info: 'Completed disk I/O operations. Keep in mind the number of operations requested might be higher, since the system is able to merge adjacent to each other (see merged operations chart).' }, 'disk.qops': { @@ -601,7 +656,7 @@ netdataDashboard.context = { }, 'disk.mops': { height: 0.5, - info: 'The number of merged disk operations. The Linux kernel is able to merge adjacent I/O operations, for example two 4KB reads can become one 8KB read before given to disk.' + info: 'The number of merged disk operations. The system is able to merge adjacent I/O operations, for example two 4KB reads can become one 8KB read before given to disk.' }, 'disk.svctm': { height: 0.5, @@ -692,7 +747,8 @@ netdataDashboard.context = { 'apache.workers': { mainheads: [ - function(id) { + function(os, id) { + void(os); return '
success includes 1xx, 2xx and 304, error includes 5xx, redirect includes 3xx except 304, bad includes 4xx, other are all the other responses.', + mainheads: [ + function(os, id) { + void(os); + return '
'; + }, + + function(os, id) { + void(os); + return '
'; + }, + + function(os, id) { + void(os); + return '
'; + }, + + function(os, id) { + void(os); + return '
'; + } + ] + }, + + 'web_log.response_codes': { + info: 'Web server responses by code family. According to the standards 1xx are informational responses, 2xx are successful responses, 3xx are redirects (although they include 304 which is used as "not modified"), 4xx are bad requests, 5xx are internal server errors, other are non-standard responses, unmatched counts the lines in the log file that are not matched by the plugin (let us know if you have any unmatched).' + }, + + 'web_log.response_time': { + mainheads: [ + function(os, id) { + void(os); + return '
'; + } + ] + }, + + 'web_log.detailed_response_codes': { + info: 'Number of responses for each response code individually.' + }, + + 'web_log.requests_per_ipproto': { + info: 'Web server requests received per IP protocol version.' + }, + + 'web_log.clients': { + info: 'Unique client IPs accessing the web server, within each data collection iteration. If data collection is per second, this chart shows unique client IPs per second.' + }, + + 'web_log.clients_all': { + info: 'Unique client IPs accessing the web server since the last restart of netdata. This plugin keeps in memory all the unique IPs that have accessed the web server. On very busy web servers (several millions of unique IPs) you may want to disable this chart (check /etc/netdata/python.d/web_log.conf).' } }; diff --git a/web/dashboard_info_custom_example.js b/web/dashboard_info_custom_example.js new file mode 100644 index 000000000..f9e255d77 --- /dev/null +++ b/web/dashboard_info_custom_example.js @@ -0,0 +1,58 @@ +/* + * Custom netdata information file + * ------------------------------- + * + * Use this file to add custom information on netdata dashboards: + * + * 1. Copy it to a new filename (so that it will not be overwritten with netdata updates) + * 2. Edit it to fit your needs + * 3. Set the following option to /etc/netdata/netdata.conf : + * + * [web] + * custom dashboard_info.js = your_filename.js + * + * Using this file you can: + * + * 1. Overwrite or add messages to menus, submenus and charts. + * Use dashboard_info.js to find out what you can define. + * + * 2. Inject javascript code into the default netdata dashboard. + * + */ + +// ---------------------------------------------------------------------------- +// MENU +// +// - title the menu title as to be rendered at the charts menu +// - icon html fragment of the icon to display +// - info html fragment for the description above all the menu charts + +customDashboard.menu = { + +}; + + +// ---------------------------------------------------------------------------- +// SUBMENU +// +// - title the submenu title as to be rendered at the charts menu +// - info html fragment for the description above all the submenu charts + +customDashboard.submenu = { + +}; + + +// ---------------------------------------------------------------------------- +// CONTEXT (the template each chart is based on) +// +// - info html fragment for the description above the chart +// - height a ratio to the default as a decimal number: 1.0 = 100% +// - colors a single color or an array of colors to use for the dimensions +// - valuerange the y-range of the chart as an array [min, max] +// - heads an array of gauge charts to render above the submenu section +// - mainheads an array of gauge charts to render at the menu section + +customDashboard.context = { + +}; diff --git a/web/index.html b/web/index.html index d8e128234..250dbfed3 100644 --- a/web/index.html +++ b/web/index.html @@ -35,7 +35,7 @@ - + @@ -508,16 +508,36 @@ // -------------------------------------------------------------------- // check options that should be processed before loading netdata.js + var localStorageTested = -1; + function localStorageTest() { + if(localStorageTested !== -1) + return localStorageTested; + + if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + var test = 'test'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + localStorageTested = true; + } + catch (e) { + localStorageTested = false; + } + } + else + localStorageTested = false; + + return localStorageTested; + } + function loadLocalStorage(name) { var ret = null; try { - if(typeof Storage !== "undefined" && typeof localStorage === 'object') + if(localStorageTest() === true) ret = localStorage.getItem(name); } - catch(error) { - ; - } + catch(error) {} if(typeof ret === 'undefined' || ret === null) return null; @@ -530,14 +550,12 @@ function saveLocalStorage(name, value) { // console.log('saving: ' + name.toString() + ' = ' + value.toString()); try { - if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + if(localStorageTest() === true) { localStorage.setItem(name, value.toString()); return true; } } - catch(error) { - ; - } + catch(error) {} return false; } @@ -579,7 +597,52 @@ var netdataRegistryCallback = function(machines_array) { var el = ''; var a1 = ''; - var found = 0; + var found = 0, hosted = 0; + var len, i, url, hostname, icon; + + if(options.hosts.length > 1) { + // there are mirrored hosts here + + el += '
  • databases available on this host
  • '; + a1 += '
  • '; + + var base = document.location.origin.toString() + document.location.pathname.toString(); + if(base.endsWith("/host/" + options.hostname + "/")) + base = base.substring(0, base.length - ("/host/" + options.hostname + "/").toString().length); + + if(base.endsWith("/")) + base = base.substring(0, base.length - 1); + + var master = options.hosts[0].hostname; + var sorted = options.hosts.sort(function(a, b) { + if(a.hostname === master) return -1; + if(a.hostname === b.hostname) return 0; + else if(a.hostname > b.hostname) return 1; + return -1; + }); + + i = 0; + len = sorted.length; + while(len--) { + hostname = sorted[i].hostname; + if(hostname == master) { + url = base + "/"; + icon = "home"; + } + else { + url = base + "/host/" + hostname + "/"; + icon = "window-restore"; + } + + el += '
  • ' + hostname + '
  • '; + a1 += '
  • '; + hosted++; + i++; + } + + el += ''; + a1 += ''; + } if(machines_array === null) { var ret = loadLocalStorage("registryCallback"); @@ -598,7 +661,7 @@ return 0; }); - var len = machines.length; + len = machines.length; while(len--) { var u = machines[len]; found++; @@ -663,10 +726,7 @@ this_is_demo = true; } } - catch(error) { - ; - } - + catch(error) {} return this_is_demo; } @@ -685,15 +745,18 @@ } function netdataReload(url) { - var t = netdataURL(url); - // console.log('netdataReload: ' + t); - document.location = t; + document.location = netdataURL(url); // since we play with hash // this is needed to reload the page location.reload(); } + function gotoHostedModalHandler(url) { + document.location = url + urlOptions.genHash(); + return false; + } + var gotoServerValidateRemaining = 0; var gotoServerMiddleClick = false; var gotoServerStop = false; @@ -830,6 +893,8 @@ var deleteRegistryUrl = null; function deleteRegistryModalHandler(guid, name, url) { + void(guid); + deleteRegistryUrl = url; document.getElementById('deleteRegistryServerName').innerHTML = name; document.getElementById('deleteRegistryServerName2').innerHTML = name; @@ -854,47 +919,28 @@ } var options = { - sparklines_registry: {}, menus: {}, submenu_names: {}, data: null, hostname: 'netdata_server', // will be overwritten by the netdata server - categories: new Array(), + version: 'unknown', + categories: [], categories_idx: {}, - families: new Array(), + families: [], families_idx: {}, + hosts: [], chartsPerRow: 0, - chartsMinWidth: 1450, - chartsHeight: 180, - sparklinesHeight: 60, + // chartsMinWidth: 1450, + chartsHeight: 180 }; - // generate a sparkline - // used in the documentation - function sparkline(chart, dimension, units) { - var key = chart + '.' + dimension; - - if(typeof units === 'undefined') - units = ''; - - if(typeof options.sparklines_registry[key] === 'undefined') - options.sparklines_registry[key] = { count: 1 }; - else - options.sparklines_registry[key].count++; - - key = key + '.' + options.sparklines_registry[key].count; - - var h = '
    (X' + units + ')'; - - return h; - } - function chartsPerRow(total) { if(options.chartsPerRow === 0) { - width = Math.floor(total / options.chartsMinWidth); - if(width === 0) width = 1; - return width; + return 1; + //var width = Math.floor(total / options.chartsMinWidth); + //if(width === 0) width = 1; + //return width; } else return options.chartsPerRow; } @@ -908,9 +954,11 @@ function sortObjectByPriority(object) { var idx = {}; - var sorted = new Array(); + var sorted = []; for(var i in object) { + if(!object.hasOwnProperty(i)) continue; + if(typeof idx[i] === 'undefined') { idx[i] = object[i]; sorted.push(i); @@ -944,11 +992,52 @@ // ---------------------------------------------------------------------------- + // user editable information + var customDashboard = { + menu: {}, + submenu: {}, + context: {} + }; + + // netdata standard information var netdataDashboard = { + sparklines_registry: {}, + os: 'unknown', + menu: {}, submenu: {}, context: {}, + // generate a sparkline + // used in the documentation + sparkline: function (prefix, chart, dimension, units, suffix) { + if(options.data === null || typeof options.data.charts === 'undefined') + return ''; + + if(typeof options.data.charts[chart] === 'undefined') + return ''; + + if(typeof options.data.charts[chart].dimensions === 'undefined') + return ''; + + if(typeof options.data.charts[chart].dimensions[dimension] === 'undefined') + return ''; + + var key = chart + '.' + dimension; + + if(typeof units === 'undefined') + units = ''; + + if(typeof this.sparklines_registry[key] === 'undefined') + this.sparklines_registry[key] = { count: 1 }; + else + this.sparklines_registry[key].count++; + + key = key + '.' + this.sparklines_registry[key].count; + + return prefix + '
    (X' + units + ')' + suffix; + }, + gaugeChart: function(title, width, dimensions, colors) { if(typeof colors === 'undefined') colors = ''; @@ -957,53 +1046,69 @@ dimensions = ''; return '
    '; + + ' data-dimensions="' + dimensions + '"' + + ' data-chart-library="gauge"' + + ' data-gauge-adjust="width"' + + ' data-title="' + title + '"' + + ' data-width="' + width + '"' + + ' data-before="0"' + + ' data-after="-CHART_DURATION"' + + ' data-points="CHART_DURATION"' + + ' data-colors="' + colors + '"' + + ' role="application">
    '; }, anyAttribute: function(obj, attr, key, def) { - if(typeof obj[key] !== 'undefined') { - if(typeof obj[key][attr] !== 'undefined') - return obj[key][attr]; + if(typeof(obj[key]) !== 'undefined') { + var x = obj[key][attr]; + + if(typeof(x) === 'undefined') + return def; + + if(typeof(x) === 'function') { + return x(netdataDashboard.os); + } + + return x; } + return def; }, menuTitle: function(chart) { if(typeof chart.menu_pattern !== 'undefined') { - return (netdataDashboard.anyAttribute(netdataDashboard.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString() + return (this.anyAttribute(this.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString() + ' ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString()).replace(/_/g, ' '); } - return (netdataDashboard.anyAttribute(netdataDashboard.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' '); + return (this.anyAttribute(this.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' '); }, menuIcon: function(chart) { if(typeof chart.menu_pattern !== 'undefined') - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'icon', chart.menu_pattern, '').toString(); + return this.anyAttribute(this.menu, 'icon', chart.menu_pattern, '').toString(); - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'icon', chart.menu, ''); + return this.anyAttribute(this.menu, 'icon', chart.menu, ''); }, - menuInfo: function(menu) { - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'info', menu, null); + menuInfo: function(chart) { + if(typeof chart.menu_pattern !== 'undefined') + return this.anyAttribute(this.menu, 'info', chart.menu_pattern, null); + + return this.anyAttribute(this.menu, 'info', chart.menu, null); }, - menuHeight: function(menu, relative) { - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'height', menu, 1.0) * relative; + menuHeight: function(chart) { + if(typeof chart.menu_pattern !== 'undefined') + return this.anyAttribute(this.menu, 'height', chart.menu_pattern, 1.0); + + return this.anyAttribute(this.menu, 'height', chart.menu, 1.0); }, submenuTitle: function(menu, submenu) { var key = menu + '.' + submenu; - var title = netdataDashboard.anyAttribute(netdataDashboard.submenu, 'title', key, submenu).toString().replace(/_/g, ' ');; + // console.log(key); + var title = this.anyAttribute(this.submenu, 'title', key, submenu).toString().replace(/_/g, ' '); if(title.length > 28) { var a = title.substring(0, 13); var b = title.substring(title.length - 12, title.length); @@ -1014,31 +1119,33 @@ submenuInfo: function(menu, submenu) { var key = menu + '.' + submenu; - return netdataDashboard.anyAttribute(netdataDashboard.submenu, 'info', key, null); + return this.anyAttribute(this.submenu, 'info', key, null); }, submenuHeight: function(menu, submenu, relative) { var key = menu + '.' + submenu; - return netdataDashboard.anyAttribute(netdataDashboard.submenu, 'height', key, 1.0) * relative; + return this.anyAttribute(this.submenu, 'height', key, 1.0) * relative; }, contextInfo: function(id) { - if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].info !== 'undefined') - return '
    ' + netdataDashboard.context[id].info + '
    '; + var x = this.anyAttribute(this.context, 'info', id, null); + + if(x !== null) + return '
    ' + x + '
    '; else return ''; }, contextValueRange: function(id) { - if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].valueRange !== 'undefined') - return netdataDashboard.context[id].valueRange; + if(typeof this.context[id] !== 'undefined' && typeof this.context[id].valueRange !== 'undefined') + return this.context[id].valueRange; else return '[null, null]'; }, contextHeight: function(id, def) { - if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].height !== 'undefined') - return def * netdataDashboard.context[id].height; + if(typeof this.context[id] !== 'undefined' && typeof this.context[id].height !== 'undefined') + return def * this.context[id].height; else return def; } @@ -1048,8 +1155,10 @@ // enrich the data structure returned by netdata // to reflect our menu system and content + // FIXME: this is a shame - we should fix charts naming (issue #807) function enrichChartData(chart) { - var tmp = chart.type.split('_')[0]; + var parts = chart.type.split('_'); + var tmp = parts[0]; switch(tmp) { case 'ap': @@ -1058,6 +1167,22 @@ chart.menu = tmp; break; + case 'apache': + chart.menu = chart.type; + if(parts.length > 2 && parts[1] === 'cache') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + + case 'bind': + chart.menu = chart.type; + if(parts.length > 2 && parts[1] === 'rndc') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + case 'cgroup': chart.menu = chart.type; if(chart.id.match(/.*[\._\/-:]qemu[\._\/-:]*/) || chart.id.match(/.*[\._\/-:]kvm[\._\/-:]*/)) @@ -1066,27 +1191,29 @@ chart.menu_pattern = 'cgroup'; break; - case 'apache': - case 'exim': - case 'dovecot': - case 'hddtemp': - case 'ipfs': - case 'memcached': - case 'mysql': - case 'named': - case 'nginx': - case 'nut': - case 'phpfpm': - case 'postfix': - case 'postgres': - case 'redis': - case 'retroshare': - case 'smawebbox': - case 'snmp': - case 'squid': - case 'tomcat': + case 'isc': chart.menu = chart.type; - chart.menu_pattern = tmp; + if(parts.length > 2 && parts[1] === 'dhcpd') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + + case 'ovpn': + chart.menu = chart.type; + if(parts.length > 3 && parts[1] === 'status' && parts[2] === 'log') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + + case 'smartd': + case 'web': + chart.menu = chart.type; + if(parts.length > 2 && parts[1] === 'log') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; break; case 'tc': @@ -1094,7 +1221,7 @@ // find a name for this device from fireqos info // we strip '_(in|out)' or '(in|out)_' - if(typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family) { + if(chart.context === 'tc.qos' && (typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family)) { var n = chart.name.split('.')[1]; if(n.endsWith('_in')) options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in')); @@ -1104,6 +1231,8 @@ options.submenu_names[chart.family] = n.slice(3, n.length); else if(n.startsWith('out_')) options.submenu_names[chart.family] = n.slice(4, n.length); + else + options.submenu_names[chart.family] = n; } // increase the priority of IFB devices @@ -1115,6 +1244,8 @@ default: chart.menu = chart.type; + if(parts.length > 1) + chart.menu_pattern = tmp; break; } @@ -1123,7 +1254,9 @@ // ---------------------------------------------------------------------------- - function headMain(charts, duration) { + function headMain(os, charts, duration) { + void(os); + var head = ''; if(typeof charts['system.swap'] !== 'undefined') @@ -1244,7 +1377,7 @@ var hi = 0, hlen = hcharts.length; while(hi < hlen) { if(typeof hcharts[hi] === 'function') - head += hcharts[hi](chart.id).replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); + head += hcharts[hi](netdataDashboard.os, chart.id).replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); else head += hcharts[hi].replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); hi++; @@ -1261,7 +1394,7 @@ var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60; var html = ''; var sidebar = ''; div.innerHTML = html; document.getElementById('sidebar').innerHTML = sidebar; @@ -1348,53 +1481,71 @@ function renderChartsAndMenu(data) { var menus = options.menus; var charts = data.charts; + var m, menu_key; for(var c in charts) { - enrichChartData(charts[c]); + if(!charts.hasOwnProperty(c)) continue; + + var chart = charts[c]; + enrichChartData(chart); + m = chart.menu; // create the menu - if(typeof menus[charts[c].menu] === 'undefined') { - menus[charts[c].menu] = { - priority: charts[c].priority, + if(typeof menus[m] === 'undefined') { + menus[m] = { + menu_pattern: chart.menu_pattern, + priority: chart.priority, submenus: {}, - title: netdataDashboard.menuTitle(charts[c]), - icon: netdataDashboard.menuIcon(charts[c]), - info: netdataDashboard.menuInfo(charts[c].menu), - height: netdataDashboard.menuHeight(charts[c].menu, options.chartsHeight) + title: netdataDashboard.menuTitle(chart), + icon: netdataDashboard.menuIcon(chart), + info: netdataDashboard.menuInfo(chart), + height: netdataDashboard.menuHeight(chart) * options.chartsHeight }; } + else { + if(typeof(menus[m].menu_pattern) === 'undefined') + menus[m].menu_pattern = chart.menu_pattern; + + if(chart.priority < menus[m].priority) + menus[m].priority = chart.priority; + } - if(charts[c].priority < menus[charts[c].menu].priority) - menus[charts[c].menu].priority = charts[c].priority; + menu_key = (typeof(menus[m].menu_pattern) !== 'undefined')?menus[m].menu_pattern:m; // create the submenu - if(typeof menus[charts[c].menu].submenus[charts[c].submenu] === 'undefined') { - menus[charts[c].menu].submenus[charts[c].submenu] = { - priority: charts[c].priority, - charts: new Array(), + if(typeof menus[m].submenus[chart.submenu] === 'undefined') { + menus[m].submenus[chart.submenu] = { + priority: chart.priority, + charts: [], title: null, - info: netdataDashboard.submenuInfo(charts[c].menu, charts[c].submenu), - height: netdataDashboard.submenuHeight(charts[c].menu, charts[c].submenu, menus[charts[c].menu].height) + info: netdataDashboard.submenuInfo(menu_key, chart.submenu), + height: netdataDashboard.submenuHeight(menu_key, chart.submenu, menus[m].height) }; } - - if(charts[c].priority < menus[charts[c].menu].submenus[charts[c].submenu].priority) - menus[charts[c].menu].submenus[charts[c].submenu].priority = charts[c].priority; + else { + if (chart.priority < menus[m].submenus[chart.submenu].priority) + menus[m].submenus[chart.submenu].priority = chart.priority; + } // index the chart in the menu/submenu - menus[charts[c].menu].submenus[charts[c].submenu].charts.push(charts[c]); + menus[m].submenus[chart.submenu].charts.push(chart); } // propagate the descriptive subname given to QoS // to all the other submenus with the same name - for(var m in menus) { + for(m in menus) { + if(!menus.hasOwnProperty(m)) continue; + for(var s in menus[m].submenus) { + if(!menus[m].submenus.hasOwnProperty(s)) continue; + // set the family using a name if(typeof options.submenu_names[s] !== 'undefined') { menus[m].submenus[s].title = s + ' (' + options.submenu_names[s] + ')'; } else { - menus[m].submenus[s].title = netdataDashboard.submenuTitle(m, s); + menu_key = (typeof(menus[m].menu_pattern) !== 'undefined')?menus[m].menu_pattern:m; + menus[m].submenus[s].title = netdataDashboard.submenuTitle(menu_key, s); } } } @@ -1423,7 +1574,7 @@ var bootstrapTableLoaded = false; function loadBootstrapTable(callback) { if(bootstrapTableLoaded === false) { - bootstrapTableLoaded === true; + bootstrapTableLoaded = true; loadJs(NETDATA.serverDefault + 'lib/bootstrap-table-1.11.0.min.js', function() { loadJs(NETDATA.serverDefault + 'lib/bootstrap-table-export-1.11.0.min.js', function() { loadJs(NETDATA.serverDefault + 'lib/tableExport-1.6.0.min.js', callback); @@ -1439,7 +1590,7 @@ var footer = '
    netdata badges refresh automatically. Their color indicates the state of the alarm:  red  is critical,  orange  is warning,  bright green  is ok,  light grey  is undefined (i.e. no data or no status),  black  is not initialized. You can copy and paste their URLs to embed them in any web page.
    netdata can send notifications for these alarms. Check this configuration file for more information.'; NETDATA.alarms.get('all', function(data) { - options.alarm_families = new Array(); + options.alarm_families = []; alarmsCallback(data); @@ -1474,70 +1625,6 @@ return t.toLocaleDateString() + space + t.toLocaleTimeString(); } - function seconds4human(seconds, options) { - var default_options = { - now: 'now', - space: ' ', - negative_suffix: 'ago', - hour: 'hour', - hours: 'hours', - minute: 'minute', - minutes: 'minutes', - second: 'second', - seconds: 'seconds', - and: 'and' - }; - - if(typeof options !== 'object') - options = default_options; - else { - var x; - for(x in default_options) { - if(typeof options[x] !== 'string') - options[x] = default_options[x]; - } - } - - if(typeof seconds === 'string') - seconds = parseInt(seconds); - - if(seconds === 0) - return options.now; - - var suffix = ''; - if(seconds < 0) { - seconds = -seconds; - if(options.negative_suffix !== '') suffix = options.space + options.negative_suffix; - } - - var hours = Math.floor(seconds / 3600); - seconds -= (hours * 3600); - - var minutes = Math.floor(seconds / 60); - seconds -= (minutes * 60); - - var txt = ''; - - if(hours > 1) txt += hours.toString() + options.space + options.hours; - else if(hours === 1) txt += hours.toString() + options.space + options.hour; - - if(hours > 0 && minutes > 0 && seconds == 0) - txt += options.space + options.and + options.space; - else if(hours > 0 && minutes > 0 && seconds > 0) - txt += ',' + options.space; - - if(minutes > 1) txt += minutes.toString() + options.space + options.minutes; - else if(minutes === 1) txt += minutes.toString() + options.space + options.minute; - - if((minutes > 0 || minutes > 0) && seconds > 0) - txt += options.space + options.and + options.space; - - if(seconds > 1) txt += Math.floor(seconds).toString() + options.space + options.seconds; - else if(seconds === 1) txt += Math.floor(seconds).toString() + options.space + options.second; - - return txt + suffix; - } - function alarm_lookup_explain(alarm, chart) { var dimensions = ' of all values '; @@ -1562,7 +1649,14 @@ function alarm_to_html(alarm, full) { var chart = options.data.charts[alarm.chart]; - var has_alarm = ((typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined')?true:false); + if(typeof(chart) === 'undefined') { + // this means the charts loaded are incomplete + // probably netdata was restarted and more charts + // are now available. + return ''; + } + + var has_alarm = (typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined'); var role_href = ((has_alarm === true)?('
     
    role: ' + alarm.recipient + '
     
      jump to chart'):(' ')); @@ -1572,10 +1666,13 @@ + ((typeof alarm.crit !== 'undefined')?('critical when' + alarm.crit + ''):''); if(full === true) { - html += ((typeof alarm.lookup_after !== 'undefined')?('db lookup' + alarm_lookup_explain(alarm, chart) + ''):'') + var units = chart.units; + if(units === '%') units = '%'; + + html += ((typeof alarm.lookup_after !== 'undefined')?('db lookup' + alarm_lookup_explain(alarm, chart) + ''):'') + ((typeof alarm.calc !== 'undefined')?('calculation' + alarm.calc + ''):'') - + ((chart.green !== null)?('green threshold' + chart.green + ' ' + chart.units + ''):'') - + ((chart.red !== null)?('red threshold' + chart.red + ' ' + chart.units + ''):''); + + ((chart.green !== null)?('green threshold' + chart.green + ' ' + units + ''):'') + + ((chart.red !== null)?('red threshold' + chart.red + ' ' + units + ''):''); } var delay = ''; @@ -1622,14 +1719,16 @@ // find the proper family of each alarm var now = Date.now(); - var x; + var x, family, alarm; var count_active = 0; var count_all = 0; var families = {}; - var families_sort = new Array(); + var families_sort = []; for(x in data.alarms) { - var alarm = data.alarms[x]; - var family = alarm.family; + if(!data.alarms.hasOwnProperty(x)) continue; + + alarm = data.alarms[x]; + family = alarm.family; // find the chart var chart = options.data.charts[alarm.chart]; @@ -1648,7 +1747,7 @@ if(typeof families[family] === 'undefined') { families[family] = { name: family, - arr: new Array(), + arr: [], priority: chart.priority }; @@ -1671,7 +1770,7 @@ var fc = 0; var len = families_sorted.length; while(len--) { - var family = families_sorted[len].name; + family = families_sorted[len].name; var active_family_added = false; var expanded = 'true'; var collapsed = ''; @@ -1680,7 +1779,7 @@ if(fc !== 0) { all += "
    "; expanded = 'false'; - collapsed = 'class="collapsed"' + collapsed = 'class="collapsed"'; cin = ''; } @@ -1693,7 +1792,7 @@ var arr = families[family].arr; var c = arr.length; while(c--) { - var alarm = arr[c]; + alarm = arr[c]; if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') { if(!active_family_added) { active_family_added = true; @@ -1726,12 +1825,13 @@ if(families_sorted.length > 0) alarm_family_show(0); // register bootstrap events - $('#alarms_all_accordion').on('show.bs.collapse', function (d) { + var $accordion = $('#alarms_all_accordion'); + $accordion.on('show.bs.collapse', function (d) { var target = $(d.target); var id = $(target).data('alarm-id'); alarm_family_show(id); }); - $('#alarms_all_accordion').on('hidden.bs.collapse', function (d) { + $accordion.on('hidden.bs.collapse', function (d) { var target = $(d.target); var id = $(target).data('alarm-id'); $('#alarm_all_' + id.toString()).html(''); @@ -1756,6 +1856,8 @@ fileName: 'netdata_alarm_log' }, rowStyle: function(row, index) { + void(index); + switch(row.status) { case 'CRITICAL' : return { classes: 'danger' }; break; case 'WARNING' : return { classes: 'warning' }; break; @@ -1776,9 +1878,8 @@ title: 'Event Date', valign: 'middle', titleTooltip: 'The date and time the even took place', - formatter: function(value, row, index) { return timestamp4human(value, ' '); }, + formatter: function(value, row, index) { void(row); void(index); return timestamp4human(value, ' '); }, align: 'center', - valign: 'middle', switchable: false, sortable: true }, @@ -1788,7 +1889,6 @@ valign: 'middle', titleTooltip: 'The host that generated this event', align: 'center', - valign: 'middle', visible: false, sortable: true }, @@ -1796,7 +1896,7 @@ field: 'unique_id', title: 'Unique ID', titleTooltip: 'The host unique ID for this event', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1806,7 +1906,7 @@ field: 'alarm_id', title: 'Alarm ID', titleTooltip: 'The ID of the alarm that generated this event', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1816,7 +1916,7 @@ field: 'alarm_event_id', title: 'Alarm Event ID', titleTooltip: 'The incremental ID of this event for the given alarm', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1845,6 +1945,8 @@ title: 'Alarm', titleTooltip: 'The alarm name that generated this event', formatter: function(value, row, index) { + void(row); + void(index); return value.toString().replace(/_/g, ' '); }, align: 'center', @@ -1852,11 +1954,30 @@ switchable: false, sortable: true }, + { + field: 'value_string', + title: 'Friendly Value', + titleTooltip: 'The value of the alarm, that triggered this event', + align: 'right', + valign: 'middle', + sortable: true + }, + { + field: 'old_value_string', + title: 'Friendly Old Value', + titleTooltip: 'The value of the alarm, just before this event', + align: 'right', + valign: 'middle', + visible: false, + sortable: true + }, { field: 'old_value', title: 'Old Value', titleTooltip: 'The value of the alarm, just before this event', formatter: function(value, row, index) { + void(row); + void(index); return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString(); }, align: 'center', @@ -1869,10 +1990,13 @@ title: 'Value', titleTooltip: 'The value of the alarm, that triggered this event', formatter: function(value, row, index) { + void(row); + void(index); return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString(); }, align: 'right', valign: 'middle', + visible: false, sortable: true }, { @@ -1881,6 +2005,7 @@ titleTooltip: 'The units of the value of the alarm', align: 'left', valign: 'middle', + visible: false, sortable: true }, { @@ -1905,7 +2030,11 @@ field: 'duration', title: 'Last Duration', titleTooltip: 'The duration the alarm was at its previous state, just before this event', - formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, + formatter: function(value, row, index) { + void(row); + void(index); + return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); + }, align: 'center', valign: 'middle', visible: false, @@ -1915,7 +2044,11 @@ field: 'non_clear_duration', title: 'Raised Duration', titleTooltip: 'The duration the alarm was raised, just before this event', - formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, + formatter: function(value, row, index) { + void(row); + void(index); + return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); + }, align: 'center', valign: 'middle', visible: false, @@ -1935,6 +2068,9 @@ title: 'Processed Status', titleTooltip: 'True when this event is processed', formatter: function(value, row, index) { + void(row); + void(index); + if(value === true) return 'DONE'; else @@ -1950,6 +2086,9 @@ title: 'Updated Status', titleTooltip: 'True when this event has been updated by another event', formatter: function(value, row, index) { + void(row); + void(index); + if(value === true) return 'UPDATED'; else @@ -1964,7 +2103,7 @@ field: 'updated_by_id', title: 'Updated By ID', titleTooltip: 'The unique ID of the event that obsoleted this one', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1974,7 +2113,7 @@ field: 'updates_id', title: 'Updates ID', titleTooltip: 'The unique ID of the event obsoleted because of this event', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1993,7 +2132,7 @@ field: 'exec_run', title: 'Script Run At', titleTooltip: 'The date and time the script has been ran', - formatter: function(value, row, index) { return timestamp4human(value, ' '); }, + formatter: function(value, row, index) { void(row); void(index); return timestamp4human(value, ' '); }, align: 'center', valign: 'middle', visible: false, @@ -2004,6 +2143,9 @@ title: 'Script Return Value', titleTooltip: 'The return code of the script', formatter: function(value, row, index) { + void(row); + void(index); + if(value === 0) return 'OK (returned 0)'; else @@ -2018,7 +2160,12 @@ field: 'delay', title: 'Script Delay', titleTooltip: 'The hysteresis of the notification', - formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, + formatter: function(value, row, index) { + void(row); + void(index); + + return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); + }, align: 'center', valign: 'middle', visible: false, @@ -2028,7 +2175,7 @@ field: 'delay_up_to_timestamp', title: 'Script Delay Run At', titleTooltip: 'The date and time the script should be run, after hysteresis', - formatter: function(value, row, index) { return timestamp4human(value, ' '); }, + formatter: function(value, row, index) { void(row); void(index); return timestamp4human(value, ' '); }, align: 'center', valign: 'middle', visible: false, @@ -2059,9 +2206,75 @@ }); } + function seconds4human(seconds, options) { + var default_options = { + now: 'now', + space: ' ', + negative_suffix: 'ago', + hour: 'hour', + hours: 'hours', + minute: 'minute', + minutes: 'minutes', + second: 'second', + seconds: 'seconds', + and: 'and' + }; + + if(typeof options !== 'object') + options = default_options; + else { + var x; + for(x in default_options) { + if(typeof options[x] !== 'string') + options[x] = default_options[x]; + } + } + + if(typeof seconds === 'string') + seconds = parseInt(seconds); + + if(seconds === 0) + return options.now; + + var suffix = ''; + if(seconds < 0) { + seconds = -seconds; + if(options.negative_suffix !== '') suffix = options.space + options.negative_suffix; + } + + var hours = Math.floor(seconds / 3600); + seconds -= (hours * 3600); + + var minutes = Math.floor(seconds / 60); + seconds -= (minutes * 60); + + var txt = ''; + + if(hours > 1) txt += hours.toString() + options.space + options.hours; + else if(hours === 1) txt += hours.toString() + options.space + options.hour; + + if(hours > 0 && minutes > 0 && seconds == 0) + txt += options.space + options.and + options.space; + else if(hours > 0 && minutes > 0 && seconds > 0) + txt += ',' + options.space; + + if(minutes > 1) txt += minutes.toString() + options.space + options.minutes; + else if(minutes === 1) txt += minutes.toString() + options.space + options.minute; + + if((minutes > 0 || minutes > 0) && seconds > 0) + txt += options.space + options.and + options.space; + + if(seconds > 1) txt += Math.floor(seconds).toString() + options.space + options.seconds; + else if(seconds === 1) txt += Math.floor(seconds).toString() + options.space + options.second; + + return txt + suffix; + } + function alarmsCallback(data) { var count = 0; for(x in data.alarms) { + if(!data.alarms.hasOwnProperty(x)) continue; + var alarm = data.alarms[x]; if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') count++; @@ -2073,6 +2286,43 @@ document.getElementById('alarms_count_badge').innerHTML = ''; } + function initializeDynamicDashboardWithData(data) { + if(data !== null) { + options.hostname = data.hostname; + options.data = data; + options.version = data.version; + netdataDashboard.os = data.os; + + if(typeof data.hosts != 'undefined') + options.hosts = data.hosts; + + // update the dashboard hostname + document.getElementById('hostname').innerHTML = options.hostname; + document.getElementById('hostname').href = NETDATA.serverDefault; + document.getElementById('netdataVersion').innerHTML = options.version; + + // update the dashboard title + document.title = options.hostname + ' netdata dashboard'; + + // close the splash screen + $("#loadOverlay").css("display","none"); + + // create a chart_by_name index + data.charts_by_name = {}; + var charts = data.charts; + var x; + for(x in charts) { + if(!charts.hasOwnProperty(x)) continue; + + var chart = charts[x]; + data.charts_by_name[chart.name] = chart; + } + + // render all charts + renderChartsAndMenu(data); + } + } + function initializeDynamicDashboard(netdata_url) { if(typeof netdata_url === 'undefined' || netdata_url === null) netdata_url = NETDATA.serverDefault; @@ -2087,31 +2337,16 @@ // download all the charts the server knows NETDATA.chartRegistry.downloadAll(netdata_url, function(data) { - if(data !== null) { - options.hostname = data.hostname; - options.data = data; - - // update the dashboard hostname - document.getElementById('hostname').innerHTML = options.hostname; - document.getElementById('hostname').href = NETDATA.serverDefault; - - // update the dashboard title - document.title = options.hostname + ' netdata dashboard'; - - // close the splash screen - $("#loadOverlay").css("display","none"); - - // create a chart_by_name index - data.charts_by_name = {}; - var charts = data.charts; - var x; - for(x in charts) { - var chart = charts[x]; - data.charts_by_name[chart.name] = chart; + if(data != null) { + if(typeof data.custom_info !== 'undefined' && data.custom_info !== "") { + loadJs(data.custom_info, function () { + $.extend(true, netdataDashboard, customDashboard); + initializeDynamicDashboardWithData(data); + }); + } + else { + initializeDynamicDashboardWithData(data); } - - // render all charts - renderChartsAndMenu(data); } }); }); @@ -2123,8 +2358,23 @@ document.getElementById('versionCheckLog').innerHTML = msg; } - function getNetdataVersion(callback) { - versionLog('Downloading installed version info from netdata...'); + function getNetdataCommitIdFromVersion() { + var s = options.version.split('-'); + + if(s.length !== 3) return null; + if(s[2][0] == 'g') { + var v = s[2].split('_')[0].substring(1, 8); + if(v.length === 7) { + versionLog('Installed git commit id of netdata is ' + v); + document.getElementById('netdataCommitId').innerHTML = v; + return v; + } + } + return null; + } + + function getNetdataCommitId(force, callback) { + versionLog('Downloading installed git commit id from netdata...'); $.ajax({ url: 'version.txt', @@ -2134,24 +2384,34 @@ }) .done(function(data) { data = data.replace(/(\r\n|\n|\r| |\t)/gm,""); - if(data.length !== 40) { - versionLog('Received version string is invalid.'); - callback(null); + + var c = getNetdataCommitIdFromVersion(); + if(c !== null && data.length === 40 && data.substring(0, 7) !== c) { + versionLog('Installed files commit id and internal netdata git commit id do not match'); + data = c; } - else { - versionLog('Installed version of netdata is ' + data); - document.getElementById('netdataVersion').innerHTML = data; + + if(data.length >= 7) { + versionLog('Installed git commit id of netdata is ' + data); + document.getElementById('netdataCommitId').innerHTML = data.substring(0, 7); callback(data); } }) .fail(function() { - versionLog('Failed to download installed version info from netdata!'); - callback(null); + versionLog('Failed to download installed git commit id from netdata!'); + + if(force === true) { + var c = getNetdataCommitIdFromVersion(); + if(c === null) versionLog('Cannot find the git commit id of netdata.'); + callback(c); + } + else + callback(null); }); } function getGithubLatestCommit(callback) { - versionLog('Downloading latest version info from github...'); + versionLog('Downloading latest git commit id info from github...'); $.ajax({ url: 'https://api.github.com/repos/firehol/netdata/commits', @@ -2159,17 +2419,17 @@ cache: false }) .done(function(data) { - versionLog('Latest version info from github is ' + data[0].sha); + versionLog('Latest git commit id from github is ' + data[0].sha); callback(data[0].sha); }) .fail(function() { - versionLog('Failed to download installed version info from github!'); + versionLog('Failed to download installed git commit id from github!'); callback(null); }); } - function checkForUpdate(callback) { - getNetdataVersion(function(sha1) { + function checkForUpdate(force, callback) { + getNetdataCommitId(force, function(sha1) { if(sha1 === null) callback(null, null); getGithubLatestCommit(function(sha2) { @@ -2199,26 +2459,26 @@ } } - checkForUpdate(function(sha1, sha2) { + checkForUpdate(force, function(sha1, sha2) { var save = false; if(sha1 === null) { save = false; - versionLog('

    Failed to get your netdata version!

    You can always get the latest version of netdata from its github page.

    '); + versionLog('

    Failed to get your netdata git commit id!

    You can always get the latest netdata from its github page.

    '); } else if(sha2 === null) { save = false; - versionLog('

    Failed to get the latest version from github.

    You can always get the latest version of netdata from its github page.

    '); + versionLog('

    Failed to get the latest git commit id from github.

    You can always get the latest netdata from its github page.

    '); } else if(sha1 === sha2) { save = true; - versionLog('

    You already have the latest version of netdata!

    No update yet?
    Probably, we need some motivation to keep going on!

    If you haven\'t already, give netdata a Star at its github page.

    '); + versionLog('

    You already have the latest netdata!

    No update yet?
    Probably, we need some motivation to keep going on!

    If you haven\'t already, give netdata a Star at its github page.

    '); } else { save = true; var compare = 'https://github.com/firehol/netdata/compare/' + sha1.toString() + '...' + sha2.toString(); - versionLog('

    New version of netdata available!

    Latest version: ' + sha2.toString() + '

    Click here for the changes log since your installed version, and
    click here for directions on updating your netdata installation.

    We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.
    Keeping your netdata updated, is generally a good idea.

    '); + versionLog('

    New version of netdata available!

    Latest commit: ' + sha2.substring(0, 7).toString() + '

    Click here for the changes log since your installed version, and
    click here for directions on updating your netdata installation.

    We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.
    Keeping your netdata updated, is generally a good idea.

    '); document.getElementById('update_badge').innerHTML = '!'; } @@ -2345,7 +2605,7 @@ { //console.log('They were open tags'); //console.log(openTags); - for (j = 0; j < openTags.length; j++) { + for (var j = 0; j < openTags.length; j++) { //console.log('Cierro tag ' + openTags[j]); bag += ''; // Close all tags that were opened @@ -2391,8 +2651,9 @@ //console.log('hash = ' + urlOptions.hash); } + var $sidebar = $('#sidebar'); /* activate bootstrap sidebar (affix) */ - $('#sidebar').affix({ + $sidebar.affix({ offset: { top: (isdemo())?150:0, bottom: 0 @@ -2402,7 +2663,7 @@ /* fix scrolling of very long affix lists http://stackoverflow.com/questions/21691585/bootstrap-3-1-0-affix-too-long */ - $('#sidebar').on('affixed.bs.affix', function() { + $sidebar.on('affixed.bs.affix', function() { $(this).removeAttr('style'); }); @@ -2413,7 +2674,7 @@ }); // change the URL based on the current position of the screen - $('#sidebar').on('activate.bs.scrollspy', function (e) { + $sidebar.on('activate.bs.scrollspy', function (e) { // console.log(e); var el = $(e.target); //if(el.find('ul').size() == 0) { @@ -2440,13 +2701,13 @@ // console.log('switching ' + option.toString()); self.bootstrapToggle(NETDATA.getOption(option)?'on':'off'); } - } + }; var theme_sync_option = function(option) { var self = $('#' + option); self.bootstrapToggle(netdataTheme === 'slate'?'on':'off'); - } + }; sync_option('eliminate_zero_dimensions'); sync_option('destroy_on_hide'); @@ -2504,19 +2765,21 @@ netdataReload(); }); - $('#updateModal').on('show.bs.modal', function() { + var $updateModal = $('#updateModal'); + $updateModal.on('show.bs.modal', function() { versionLog('checking, please wait...'); }); - $('#updateModal').on('shown.bs.modal', function() { + $updateModal.on('shown.bs.modal', function() { notifyForUpdate(true); }); - $('#alarmsModal').on('shown.bs.modal', function() { + var $alarmsModal = $('#alarmsModal'); + $alarmsModal.on('shown.bs.modal', function() { NETDATA.pause(alarmsUpdateModal); }); - $('#alarmsModal').on('hidden.bs.modal', function() { + $alarmsModal.on('hidden.bs.modal', function() { NETDATA.unpause(); document.getElementById('alarms_active').innerHTML = document.getElementById('alarms_all').innerHTML = @@ -2632,7 +2895,7 @@ }); NETDATA.requiredJs.push({ - url: NETDATA.serverDefault + 'dashboard_info.js?v20170115-1', + url: NETDATA.serverDefault + 'dashboard_info.js?v20170308-1', async: false, isAlreadyLoaded: function() { return false; } }); @@ -3144,7 +3407,8 @@ - + diff --git a/web/lib/gauge-1.3.2.min.js b/web/lib/gauge-1.3.2.min.js new file mode 100644 index 000000000..be327fe58 --- /dev/null +++ b/web/lib/gauge-1.3.2.min.js @@ -0,0 +1 @@ +(function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p=[].slice,q={}.hasOwnProperty,r=function(a,b){function d(){this.constructor=a}for(var c in b)q.call(b,c)&&(a[c]=b[c]);return d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype,a};!function(){var a,b,c,d,e,f,g;for(g=["ms","moz","webkit","o"],c=0,e=g.length;ce;c=0<=e?++d:--d)a=this.charCodeAt(c),b=(b<<5)-b+a,b&=b;return b},o=function(a){var b,c;for(b=Math.floor(a/3600),c=Math.floor((a-3600*b)/60),a-=3600*b+60*c,a+="",c+="";c.length<2;)c="0"+c;for(;a.length<2;)a="0"+a;return b=b?b+":":"",b+c+":"+a},m=function(){var a,b,c;return b=1<=arguments.length?p.call(arguments,0):[],c=b[0],a=b[1],k(c.toFixed(a))},n=function(a,b){var c,d,e;d={};for(c in a)q.call(a,c)&&(e=a[c],d[c]=e);for(c in b)q.call(b,c)&&(e=b[c],d[c]=e);return d},k=function(a){var b,c,d,e;for(a+="",c=a.split("."),d=c[0],e="",c.length>1&&(e="."+c[1]),b=/(\d+)(\d{3})/;b.test(d);)d=d.replace(b,"$1,$2");return d+e},l=function(a){return"#"===a.charAt(0)?a.substring(1,7):a},j=function(){function a(a,b){null==a&&(a=!0),this.clear=null==b||b,a&&AnimationUpdater.add(this)}return a.prototype.animationSpeed=32,a.prototype.update=function(a){var b;return null==a&&(a=!1),!(!a&&this.displayedValue===this.value)&&(this.ctx&&this.clear&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),b=this.value-this.displayedValue,Math.abs(b/this.animationSpeed)<=.001?this.displayedValue=this.value:this.displayedValue=this.displayedValue+b/this.animationSpeed,this.render(),!0)},a}(),e=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}return r(b,a),b.prototype.displayScale=1,b.prototype.setTextField=function(a,b){return this.textField=a instanceof i?a:new i(a,b)},b.prototype.setMinValue=function(a,b){var c,d,e,f,g;if(this.minValue=a,null==b&&(b=!0),b){for(this.displayedValue=this.minValue,f=this.gp||[],g=[],d=0,e=f.length;d.5&&(this.options.angle=.5),this.configDisplayScale(),this},b.prototype.configDisplayScale=function(){var a,b,c,d,e;return d=this.displayScale,this.options.highDpiSupport===!1?delete this.displayScale:(b=window.devicePixelRatio||1,a=this.ctx.webkitBackingStorePixelRatio||this.ctx.mozBackingStorePixelRatio||this.ctx.msBackingStorePixelRatio||this.ctx.oBackingStorePixelRatio||this.ctx.backingStorePixelRatio||1,this.displayScale=b/a),this.displayScale!==d&&(e=this.canvas.G__width||this.canvas.width,c=this.canvas.G__height||this.canvas.height,this.canvas.width=e*this.displayScale,this.canvas.height=c*this.displayScale,this.canvas.style.width=e+"px",this.canvas.style.height=c+"px",this.canvas.G__width=e,this.canvas.G__height=c),this},b}(j),i=function(){function a(a,b){this.el=a,this.fractionDigits=b}return a.prototype.render=function(a){return this.el.innerHTML=m(a.displayedValue,this.fractionDigits)},a}(),a=function(a){function b(a,b){this.elem=a,this.text=null!=b&&b,this.value=1*this.elem.innerHTML,this.text&&(this.value=0)}return r(b,a),b.prototype.displayedValue=0,b.prototype.value=0,b.prototype.setVal=function(a){return this.value=1*a},b.prototype.render=function(){var a;return a=this.text?o(this.displayedValue.toFixed(0)):k(m(this.displayedValue)),this.elem.innerHTML=a},b}(j),b={create:function(b){var c,d,e,f;for(f=[],d=0,e=b.length;d=e;c=0<=e?++d:--d)g=parseInt(l(this.options.percentColors[c][1]).substring(0,2),16),b=parseInt(l(this.options.percentColors[c][1]).substring(2,4),16),a=parseInt(l(this.options.percentColors[c][1]).substring(4,6),16),f.push(this.percentColors[c]={pct:this.options.percentColors[c][0],color:{r:g,g:b,b:a}});return f}},b.prototype.set=function(a){var b,c,d,e,f,g,i;if(a instanceof Array||(a=[a]),a.length>this.gp.length)for(c=d=0,g=a.length-this.gp.length;0<=g?dg;c=0<=g?++d:--d)b=new h(this),b.setOptions(this.options.pointer),this.gp.push(b);else a.lengththis.maxValue?this.options.limitMax?i=this.maxValue:this.maxValue=i+1:i=h;e=0<=h?++f:--f)if(a<=this.percentColors[e].pct){b===!0?(i=this.percentColors[e-1]||this.percentColors[0],d=this.percentColors[e],g=(a-i.pct)/(d.pct-i.pct),c={r:Math.floor(i.color.r*(1-g)+d.color.r*g),g:Math.floor(i.color.g*(1-g)+d.color.g*g),b:Math.floor(i.color.b*(1-g)+d.color.b*g)}):c=this.percentColors[e].color;break}return"rgb("+[c.r,c.g,c.b].join(",")+")"},b.prototype.getColorForValue=function(a,b){var c;return c=(a-this.minValue)/(this.maxValue-this.minValue),this.getColorForPercentage(c,b)},b.prototype.renderStaticLabels=function(a,b,c,d){var e,f,g,h,i,j,k,l,n,o;for(this.ctx.save(),this.ctx.translate(b,c),e=a.font||"10px Times",j=/\d+\.?\d?/,i=e.match(j)[0],l=e.slice(i.length),f=parseFloat(i)*this.displayScale,this.ctx.font=f+l,this.ctx.fillStyle=a.color||"#000000",this.ctx.textBaseline="bottom",this.ctx.textAlign="center",k=a.labels,g=0,h=k.length;g=this.minValue)&&(!this.options.limitMax||o<=this.maxValue)&&(n=this.getAngle(o)-3*Math.PI/2,this.ctx.rotate(n),this.ctx.fillText(m(o,a.fractionDigits),0,-d-this.lineWidth/2),this.ctx.rotate(-n));return this.ctx.restore()},b.prototype.render=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;if(n=this.canvas.width/2,d=this.canvas.height*this.paddingTop+this.availableHeight-(this.radius+this.lineWidth/2)*this.extraPadding,a=this.getAngle(this.displayedValue),this.textField&&this.textField.render(this),this.ctx.lineCap="butt",k=this.radius*this.options.radiusScale,this.options.staticLabels&&this.renderStaticLabels(this.options.staticLabels,n,d,k),this.options.staticZones){for(this.ctx.save(),this.ctx.translate(n,d),this.ctx.lineWidth=this.lineWidth,l=this.options.staticZones,e=0,g=l.length;ethis.maxValue&&(i=this.maxValue),this.ctx.strokeStyle=o.strokeStyle,this.ctx.beginPath(),this.ctx.arc(0,0,k,this.getAngle(j),this.getAngle(i),!1),this.ctx.stroke();this.ctx.restore()}else void 0!==this.options.customFillStyle?b=this.options.customFillStyle(this):null!==this.percentColors?b=this.getColorForValue(this.displayedValue,!0):void 0!==this.options.colorStop?(b=0===this.options.gradientType?this.ctx.createRadialGradient(n,d,9,n,d,70):this.ctx.createLinearGradient(0,0,n,0),b.addColorStop(0,this.options.colorStart),b.addColorStop(1,this.options.colorStop)):b=this.options.colorStart,this.ctx.strokeStyle=b,this.ctx.beginPath(),this.ctx.arc(n,d,k,(1+this.options.angle)*Math.PI,a,!1),this.ctx.lineWidth=this.lineWidth,this.ctx.stroke(),this.ctx.strokeStyle=this.options.strokeColor,this.ctx.beginPath(),this.ctx.arc(n,d,k,a,(2-this.options.angle)*Math.PI,!1),this.ctx.stroke();for(this.ctx.translate(n,d),m=this.gp,f=0,h=m.length;fthis.maxValue&&(this.maxValue=1.1*this.value),AnimationUpdater.run()},b.prototype.render=function(){var a,b,c,d,e,f;return a=this.getAngle(this.displayedValue),f=this.canvas.width/2,c=this.canvas.height/2,this.textField&&this.textField.render(this),b=this.ctx.createRadialGradient(f,c,39,f,c,70),b.addColorStop(0,this.options.colorStart),b.addColorStop(1,this.options.colorStop),d=this.radius-this.lineWidth/2,e=this.radius+this.lineWidth/2,this.ctx.strokeStyle=this.options.strokeColor,this.ctx.beginPath(),this.ctx.arc(f,c,this.radius,(1-this.options.angle)*Math.PI,(2+this.options.angle)*Math.PI,!1),this.ctx.lineWidth=this.lineWidth,this.ctx.lineCap="round",this.ctx.stroke(),this.ctx.strokeStyle=b,this.ctx.beginPath(),this.ctx.arc(f,c,this.radius,(1-this.options.angle)*Math.PI,a,!1),this.ctx.stroke()},b}(e),f=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}return r(b,a),b.prototype.strokeGradient=function(a,b,c,d){var e;return e=this.ctx.createRadialGradient(a,b,c,a,b,d),e.addColorStop(0,this.options.shadowColor),e.addColorStop(.12,this.options._orgStrokeColor),e.addColorStop(.88,this.options._orgStrokeColor),e.addColorStop(1,this.options.shadowColor),e},b.prototype.setOptions=function(a){var c,d,e,f;return null==a&&(a=null),b.__super__.setOptions.call(this,a),f=this.canvas.width/2,c=this.canvas.height/2,d=this.radius-this.lineWidth/2,e=this.radius+this.lineWidth/2,this.options._orgStrokeColor=this.options.strokeColor,this.options.strokeColor=this.strokeGradient(f,c,d,e),this},b}(d),window.AnimationUpdater={elements:[],animId:null,addAll:function(a){var b,c,d,e;for(e=[],c=0,d=a.length;cc&&(f=g[c],!window.requestAnimationFrame);c++)window.requestAnimationFrame=window[f+"RequestAnimationFrame"],window.cancelAnimationFrame=window[f+"CancelAnimationFrame"]||window[f+"CancelRequestAnimationFrame"];return a=null,d=0,b={},requestAnimationFrame?window.cancelAnimationFrame?void 0:(a=window.requestAnimationFrame,window.requestAnimationFrame=function(c,e){var f;return f=++d,a(function(){return b[f]?void 0:c()},e),f},window.cancelAnimationFrame=function(a){return b[a]=!0}):(window.requestAnimationFrame=function(a,b){var c,d,e,f;return c=(new Date).getTime(),f=Math.max(0,16-(c-e)),d=window.setTimeout(function(){return a(c+f)},f),e=c+f,d},window.cancelAnimationFrame=function(a){return clearTimeout(a)})}(),String.prototype.hashCode=function(){var a,b,c,d,e;if(b=0,0===this.length)return b;for(c=d=0,e=this.length;e>=0?e>d:d>e;c=e>=0?++d:--d)a=this.charCodeAt(c),b=(b<<5)-b+a,b&=b;return b},o=function(a){var b,c;for(b=Math.floor(a/3600),c=Math.floor((a-3600*b)/60),a-=3600*b+60*c,a+="",c+="";c.length<2;)c="0"+c;for(;a.length<2;)a="0"+a;return b=b?b+":":"",b+c+":"+a},m=function(a){return k(a.toFixed(0))},p=function(a,b){var c,d;for(c in b)q.call(b,c)&&(d=b[c],a[c]=d);return a},n=function(a,b){var c,d,e;d={};for(c in a)q.call(a,c)&&(e=a[c],d[c]=e);for(c in b)q.call(b,c)&&(e=b[c],d[c]=e);return d},k=function(a){var b,c,d,e;for(a+="",c=a.split("."),d=c[0],e="",c.length>1&&(e="."+c[1]),b=/(\d+)(\d{3})/;b.test(d);)d=d.replace(b,"$1,$2");return d+e},l=function(a){return"#"===a.charAt(0)?a.substring(1,7):a},j=function(){function a(a,b){null==a&&(a=!0),this.clear=null!=b?b:!0,a&&AnimationUpdater.add(this)}return a.prototype.animationSpeed=32,a.prototype.update=function(a){var b;return null==a&&(a=!1),a||this.displayedValue!==this.value?(this.ctx&&this.clear&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),b=this.value-this.displayedValue,Math.abs(b/this.animationSpeed)<=.001?this.displayedValue=this.value:this.displayedValue=this.displayedValue+b/this.animationSpeed,this.render(),!0):!1},a}(),e=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}return r(b,a),b.prototype.displayScale=1,b.prototype.setTextField=function(a){return this.textField=a instanceof i?a:new i(a)},b.prototype.setMinValue=function(a,b){var c,d,e,f,g;if(this.minValue=a,null==b&&(b=!0),b){for(this.displayedValue=this.minValue,f=this.gp||[],g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(c.displayedValue=this.minValue);return g}},b.prototype.setOptions=function(a){return null==a&&(a=null),this.options=n(this.options,a),this.textField&&(this.textField.el.style.fontSize=a.fontSize+"px"),this.options.angle>.5&&(this.gauge.options.angle=.5),this.configDisplayScale(),this},b.prototype.configDisplayScale=function(){var a,b,c,d,e;return d=this.displayScale,this.options.highDpiSupport===!1?delete this.displayScale:(b=window.devicePixelRatio||1,a=this.ctx.webkitBackingStorePixelRatio||this.ctx.mozBackingStorePixelRatio||this.ctx.msBackingStorePixelRatio||this.ctx.oBackingStorePixelRatio||this.ctx.backingStorePixelRatio||1,this.displayScale=b/a),this.displayScale!==d&&(e=this.canvas.G__width||this.canvas.width,c=this.canvas.G__height||this.canvas.height,this.canvas.width=e*this.displayScale,this.canvas.height=c*this.displayScale,this.canvas.style.width=e+"px",this.canvas.style.height=c+"px",this.canvas.G__width=e,this.canvas.G__height=c),this},b}(j),i=function(){function a(a){this.el=a}return a.prototype.render=function(a){return this.el.innerHTML=m(a.displayedValue)},a}(),a=function(a){function b(a,b){this.elem=a,this.text=null!=b?b:!1,this.value=1*this.elem.innerHTML,this.text&&(this.value=0)}return r(b,a),b.prototype.displayedValue=0,b.prototype.value=0,b.prototype.setVal=function(a){return this.value=1*a},b.prototype.render=function(){var a;return a=this.text?o(this.displayedValue.toFixed(0)):k(m(this.displayedValue)),this.elem.innerHTML=a},b}(j),b={create:function(b){var c,d,e,f;for(f=[],d=0,e=b.length;e>d;d++)c=b[d],f.push(new a(c));return f}},h=function(a){function b(a){this.gauge=a,this.ctx=this.gauge.ctx,this.canvas=this.gauge.canvas,b.__super__.constructor.call(this,!1,!1),this.setOptions()}return r(b,a),b.prototype.displayedValue=0,b.prototype.value=0,b.prototype.options={strokeWidth:.035,length:.1,color:"#000000"},b.prototype.setOptions=function(a){return null==a&&(a=null),p(this.options,a),this.length=this.canvas.height*this.options.length,this.strokeWidth=this.canvas.height*this.options.strokeWidth,this.maxValue=this.gauge.maxValue,this.minValue=this.gauge.minValue,this.animationSpeed=this.gauge.animationSpeed,this.options.angle=this.gauge.options.angle},b.prototype.render=function(){var a,b,c,d,e,f,g,h,i;return a=this.gauge.getAngle.call(this,this.displayedValue),b=this.canvas.width/2,c=.9*this.canvas.height,h=Math.round(b+this.length*Math.cos(a)),i=Math.round(c+this.length*Math.sin(a)),f=Math.round(b+this.strokeWidth*Math.cos(a-Math.PI/2)),g=Math.round(c+this.strokeWidth*Math.sin(a-Math.PI/2)),d=Math.round(b+this.strokeWidth*Math.cos(a+Math.PI/2)),e=Math.round(c+this.strokeWidth*Math.sin(a+Math.PI/2)),this.ctx.fillStyle=this.options.color,this.ctx.beginPath(),this.ctx.arc(b,c,this.strokeWidth,0,2*Math.PI,!0),this.ctx.fill(),this.ctx.beginPath(),this.ctx.moveTo(f,g),this.ctx.lineTo(h,i),this.ctx.lineTo(d,e),this.ctx.fill()},b}(j),c=function(){function a(a){this.elem=a}return a.prototype.updateValues=function(a){return this.value=a[0],this.maxValue=a[1],this.avgValue=a[2],this.render()},a.prototype.render=function(){var a,b;return this.textField&&this.textField.text(m(this.value)),0===this.maxValue&&(this.maxValue=2*this.avgValue),b=this.value/this.maxValue*100,a=this.avgValue/this.maxValue*100,$(".bar-value",this.elem).css({width:b+"%"}),$(".typical-value",this.elem).css({width:a+"%"})},a}(),g=function(a){function b(a){this.canvas=a,b.__super__.constructor.call(this),this.percentColors=null,"undefined"!=typeof G_vmlCanvasManager&&(this.canvas=window.G_vmlCanvasManager.initElement(this.canvas)),this.ctx=this.canvas.getContext("2d"),this.gp=[new h(this)],this.setOptions(),this.render()}return r(b,a),b.prototype.elem=null,b.prototype.value=[20],b.prototype.maxValue=80,b.prototype.minValue=0,b.prototype.displayedAngle=0,b.prototype.displayedValue=0,b.prototype.lineWidth=40,b.prototype.paddingBottom=.1,b.prototype.percentColors=null,b.prototype.options={colorStart:"#6fadcf",colorStop:void 0,gradientType:0,strokeColor:"#e0e0e0",pointer:{length:.8,strokeWidth:.035},angle:.15,lineWidth:.44,fontSize:40,limitMax:!1},b.prototype.setOptions=function(a){var c,d,e,f;for(null==a&&(a=null),b.__super__.setOptions.call(this,a),this.configPercentColors(),this.lineWidth=this.canvas.height*(1-this.paddingBottom)*this.options.lineWidth,this.radius=this.canvas.height*(1-this.paddingBottom)-this.lineWidth,this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.render(),f=this.gp,d=0,e=f.length;e>d;d++)c=f[d],c.setOptions(this.options.pointer),c.render();return this},b.prototype.configPercentColors=function(){var a,b,c,d,e,f,g;if(this.percentColors=null,void 0!==this.options.percentColors){for(this.percentColors=new Array,f=[],c=d=0,e=this.options.percentColors.length-1;e>=0?e>=d:d>=e;c=e>=0?++d:--d)g=parseInt(l(this.options.percentColors[c][1]).substring(0,2),16),b=parseInt(l(this.options.percentColors[c][1]).substring(2,4),16),a=parseInt(l(this.options.percentColors[c][1]).substring(4,6),16),f.push(this.percentColors[c]={pct:this.options.percentColors[c][0],color:{r:g,g:b,b:a}});return f}},b.prototype.set=function(a){var b,c,d,e,f,g,i;if(a instanceof Array||(a=[a]),a.length>this.gp.length)for(b=c=0,g=a.length-this.gp.length;g>=0?g>c:c>g;b=g>=0?++c:--c)this.gp.push(new h(this));for(b=0,f=!1,d=0,e=a.length;e>d;d++)i=a[d],i>this.maxValue&&(this.maxValue=1.1*this.value,f=!0),this.gp[b].value=i,this.gp[b++].setOptions({maxValue:this.maxValue,angle:this.options.angle});return this.value=a[a.length-1],f&&this.options.limitMax?void 0:AnimationUpdater.run()},b.prototype.getAngle=function(a){return(1+this.options.angle)*Math.PI+(a-this.minValue)/(this.maxValue-this.minValue)*(1-2*this.options.angle)*Math.PI},b.prototype.getColorForPercentage=function(a,b){var c,d,e,f,g,h,i;if(0===a)c=this.percentColors[0].color;else for(c=this.percentColors[this.percentColors.length-1].color,e=f=0,h=this.percentColors.length-1;h>=0?h>=f:f>=h;e=h>=0?++f:--f)if(a<=this.percentColors[e].pct){b===!0?(i=this.percentColors[e-1],d=this.percentColors[e],g=(a-i.pct)/(d.pct-i.pct),c={r:Math.floor(i.color.r*(1-g)+d.color.r*g),g:Math.floor(i.color.g*(1-g)+d.color.g*g),b:Math.floor(i.color.b*(1-g)+d.color.b*g)}):c=this.percentColors[e].color;break}return"rgb("+[c.r,c.g,c.b].join(",")+")"},b.prototype.getColorForValue=function(a,b){var c;return c=(a-this.minValue)/(this.maxValue-this.minValue),this.getColorForPercentage(c,b)},b.prototype.render=function(){var a,b,c,d,e,f,g,h,i;for(i=this.canvas.width/2,d=this.canvas.height*(1-this.paddingBottom),a=this.getAngle(this.displayedValue),this.textField&&this.textField.render(this),this.ctx.lineCap="butt",void 0!==this.options.customFillStyle?b=this.options.customFillStyle(this):null!==this.percentColors?b=this.getColorForValue(this.displayedValue,!0):void 0!==this.options.colorStop?(b=0===this.options.gradientType?this.ctx.createRadialGradient(i,d,9,i,d,70):this.ctx.createLinearGradient(0,0,i,0),b.addColorStop(0,this.options.colorStart),b.addColorStop(1,this.options.colorStop)):b=this.options.colorStart,this.ctx.strokeStyle=b,this.ctx.beginPath(),this.ctx.arc(i,d,this.radius,(1+this.options.angle)*Math.PI,a,!1),this.ctx.lineWidth=this.lineWidth,this.ctx.stroke(),this.ctx.strokeStyle=this.options.strokeColor,this.ctx.beginPath(),this.ctx.arc(i,d,this.radius,a,(2-this.options.angle)*Math.PI,!1),this.ctx.stroke(),g=this.gp,h=[],e=0,f=g.length;f>e;e++)c=g[e],h.push(c.update(!0));return h},b}(e),d=function(a){function b(a){this.canvas=a,b.__super__.constructor.call(this),"undefined"!=typeof G_vmlCanvasManager&&(this.canvas=window.G_vmlCanvasManager.initElement(this.canvas)),this.ctx=this.canvas.getContext("2d"),this.setOptions(),this.render()}return r(b,a),b.prototype.lineWidth=15,b.prototype.displayedValue=0,b.prototype.value=33,b.prototype.maxValue=80,b.prototype.minValue=0,b.prototype.options={lineWidth:.1,colorStart:"#6f6ea0",colorStop:"#c0c0db",strokeColor:"#eeeeee",shadowColor:"#d5d5d5",angle:.35},b.prototype.getAngle=function(a){return(1-this.options.angle)*Math.PI+(a-this.minValue)/(this.maxValue-this.minValue)*(2+this.options.angle-(1-this.options.angle))*Math.PI},b.prototype.setOptions=function(a){return null==a&&(a=null),b.__super__.setOptions.call(this,a),this.lineWidth=this.canvas.height*this.options.lineWidth,this.radius=this.canvas.height/2-this.lineWidth/2,this},b.prototype.set=function(a){return this.value=a,this.value>this.maxValue&&(this.maxValue=1.1*this.value),AnimationUpdater.run()},b.prototype.render=function(){var a,b,c,d,e,f;return a=this.getAngle(this.displayedValue),f=this.canvas.width/2,c=this.canvas.height/2,this.textField&&this.textField.render(this),b=this.ctx.createRadialGradient(f,c,39,f,c,70),b.addColorStop(0,this.options.colorStart),b.addColorStop(1,this.options.colorStop),d=this.radius-this.lineWidth/2,e=this.radius+this.lineWidth/2,this.ctx.strokeStyle=this.options.strokeColor,this.ctx.beginPath(),this.ctx.arc(f,c,this.radius,(1-this.options.angle)*Math.PI,(2+this.options.angle)*Math.PI,!1),this.ctx.lineWidth=this.lineWidth,this.ctx.lineCap="round",this.ctx.stroke(),this.ctx.strokeStyle=b,this.ctx.beginPath(),this.ctx.arc(f,c,this.radius,(1-this.options.angle)*Math.PI,a,!1),this.ctx.stroke()},b}(e),f=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}return r(b,a),b.prototype.strokeGradient=function(a,b,c,d){var e;return e=this.ctx.createRadialGradient(a,b,c,a,b,d),e.addColorStop(0,this.options.shadowColor),e.addColorStop(.12,this.options._orgStrokeColor),e.addColorStop(.88,this.options._orgStrokeColor),e.addColorStop(1,this.options.shadowColor),e},b.prototype.setOptions=function(a){var c,d,e,f;return null==a&&(a=null),b.__super__.setOptions.call(this,a),f=this.canvas.width/2,c=this.canvas.height/2,d=this.radius-this.lineWidth/2,e=this.radius+this.lineWidth/2,this.options._orgStrokeColor=this.options.strokeColor,this.options.strokeColor=this.strokeGradient(f,c,d,e),this},b}(d),window.AnimationUpdater={elements:[],animId:null,addAll:function(a){var b,c,d,e;for(e=[],c=0,d=a.length;d>c;c++)b=a[c],e.push(AnimationUpdater.elements.push(b));return e},add:function(a){return AnimationUpdater.elements.push(a)},run:function(){var a,b,c,d,e;for(a=!0,e=AnimationUpdater.elements,c=0,d=e.length;d>c;c++)b=e[c],b.update()&&(a=!1);return a?cancelAnimationFrame(AnimationUpdater.animId):AnimationUpdater.animId=requestAnimationFrame(AnimationUpdater.run)}},"function"==typeof window.define&&null!=window.define.amd?define(function(){return{Gauge:g,Donut:f,BaseDonut:d,TextRenderer:i}}):"undefined"!=typeof module&&null!=module.exports?module.exports={Gauge:g,Donut:f,BaseDonut:d,TextRenderer:i}:(window.Gauge=g,window.Donut=f,window.BaseDonut=d,window.TextRenderer=i)}).call(this); \ No newline at end of file diff --git a/web/netdata-swagger.json b/web/netdata-swagger.json index ad424abad..404944d12 100644 --- a/web/netdata-swagger.json +++ b/web/netdata-swagger.json @@ -3,7 +3,7 @@ "info": { "title": "NetData API", "description": "Real time data collection and graphs...", - "version": "1.4.1_master" + "version": "1.5.1_rolling" }, "host": "registry.my-netdata.io", "schemes": [ @@ -396,6 +396,34 @@ } } } + }, + "/allmetrics": { + "get": { + "summary": "Get a value of all the metrics maintained by netdata", + "description": "The charts endpoint returns the latest value of all charts and dimensions stored in the netdata server.", + "parameters": [ + { + "name": "format", + "in": "query", + "description": "The format of the response to be returned", + "required": true, + "type": "string", + "enum": [ + "shell", + "prometheus" + ], + "default": "shell" + } + ], + "responses": { + "200": { + "description": "All the metrics returned in the format requested" + }, + "400": { + "description": "The format requested is not supported" + } + } + } } }, "definitions": { @@ -406,6 +434,23 @@ "type": "string", "description": "The hostname of the netdata server." }, + "version": { + "type": "string", + "description": "netdata version of the server." + }, + "os": { + "type": "string", + "description": "The netdata server host operating system.", + "enum": [ + "macos", + "linux", + "freebsd" + ] + }, + "history": { + "type": "number", + "description": "The duration, in seconds, of the round robin database maintained by netdata." + }, "update_every": { "type": "number", "description": "The default update frequency of the netdata server. All charts have an update frequency equal or bigger than this." @@ -418,6 +463,22 @@ "$ref": "#/definitions/chart" } } + }, + "charts_count": { + "type": "number", + "description": "The number of charts." + }, + "dimensions_count": { + "type": "number", + "description": "The total number of dimensions." + }, + "alarms_count": { + "type": "number", + "description": "The number of alarms." + }, + "rrd_memory_bytes": { + "type": "number", + "description": "The size of the round robin database in bytes." } } }, @@ -493,6 +554,14 @@ "$ref": "#/definitions/dimension" } } + }, + "green": { + "type": "number", + "description": "Chart health green threshold" + }, + "red": { + "type": "number", + "description": "Chart health red trheshold" } } }, diff --git a/web/netdata-swagger.yaml b/web/netdata-swagger.yaml index 00a038ce7..a74d66f5d 100644 --- a/web/netdata-swagger.yaml +++ b/web/netdata-swagger.yaml @@ -2,7 +2,7 @@ swagger: '2.0' info: title: NetData API description: 'Real time data collection and graphs...' - version: 1.4.1_master + version: 1.5.1_rolling host: registry.my-netdata.io schemes: - http @@ -283,6 +283,16 @@ definitions: hostname: type: string description: 'The hostname of the netdata server.' + version: + type: string + description: 'netdata version of the server.' + os: + type: string + description: 'The netdata server host operating system.' + enum: [ 'macos', 'linux', 'freebsd' ] + history: + type: number + description: 'The duration, in seconds, of the round robin database maintained by netdata.' update_every: type: number description: 'The default update frequency of the netdata server. All charts have an update frequency equal or bigger than this.' @@ -292,6 +302,18 @@ definitions: properties: key: $ref: '#/definitions/chart' + charts_count: + type: number + description: 'The number of charts.' + dimensions_count: + type: number + description: 'The total number of dimensions.' + alarms_count: + type: number + description: 'The number of alarms.' + rrd_memory_bytes: + type: number + description: 'The size of the round robin database in bytes.' chart: type: object properties: @@ -344,13 +366,19 @@ definitions: properties: key: $ref: '#/definitions/dimension' + green: + type: number + description: 'Chart health green threshold' + red: + type: number + description: 'Chart health red trheshold' dimension: type: object properties: name: type: string description: 'The name of the dimension' - + json_wrap: type: object properties: diff --git a/web/registry.html b/web/registry.html index 3af60a84b..f40f5f2af 100644 --- a/web/registry.html +++ b/web/registry.html @@ -12,7 +12,7 @@ - + diff --git a/web/tv.html b/web/tv.html index bd55e852f..04cc01ccb 100644 --- a/web/tv.html +++ b/web/tv.html @@ -12,7 +12,7 @@ - + diff --git a/web/version.txt b/web/version.txt index 58abac87d..a7ffee85b 100644 --- a/web/version.txt +++ b/web/version.txt @@ -1 +1 @@ -3bd41a09fccccbc6b095805556d3009b9ebf6213 +f5fa346a188e906a8f2cce3c2cf32a88ce81c666 -- cgit v1.2.3 From a133c9c3b637b1dbe7b5b053f7e2572c1950cead Mon Sep 17 00:00:00 2001 From: Lennart Weller Date: Thu, 27 Jul 2017 11:55:47 +0200 Subject: New upstream version 1.7.0+dfsg --- .gitignore | 3 + .travis/deploy-if-have-key | 7 + ChangeLog | 52 + LICENSE | 674 ++++++++ LICENSE-REDISTRIBUTED.md | 150 ++ LICENSE.md | 153 +- Makefile.am | 23 + Makefile.in | 352 ++-- README.md | 19 +- aclocal.m4 | 706 +++++--- charts.d/Makefile.in | 114 +- compile | 347 ++++ conf.d/Makefile.am | 13 + conf.d/Makefile.in | 199 ++- conf.d/apps_groups.conf | 38 +- conf.d/fping.conf | 2 +- conf.d/health.d/fping.conf | 2 +- conf.d/health.d/lighttpd.conf | 14 + conf.d/health.d/mongodb.conf | 13 + conf.d/health.d/net.conf | 6 +- conf.d/health.d/ram.conf | 9 +- conf.d/health.d/tcp_resets.conf | 8 +- conf.d/health.d/web_log.conf | 3 +- conf.d/health.d/zfs.conf | 10 + conf.d/health_alarm_notify.conf | 72 + conf.d/node.d/fronius.conf.md | 67 + conf.d/python.d.conf | 9 +- conf.d/python.d/dns_query_time.conf | 72 + conf.d/python.d/elasticsearch.conf | 17 +- conf.d/python.d/fail2ban.conf | 11 +- conf.d/python.d/go_expvar.conf | 106 ++ conf.d/python.d/isc_dhcpd.conf | 11 +- conf.d/python.d/postgres.conf | 1 + conf.d/python.d/rabbitmq.conf | 75 + conf.d/python.d/samba.conf | 58 + conf.d/python.d/smartd_log.conf | 8 +- conf.d/python.d/web_log.conf | 48 +- conf.d/statsd.d/example.conf | 65 + conf.d/stream.conf | 95 +- config.guess | 184 +- config.h.in | 3 + config.sub | 106 +- configs.signatures | 49 + configure | 676 ++++++-- configure.ac | 29 +- contrib/Makefile.am | 1 + contrib/Makefile.in | 103 +- contrib/debian/changelog | 3 + contrib/debian/compat | 1 + contrib/debian/control | 25 + contrib/debian/control.wheezy | 25 + contrib/debian/copyright | 10 + contrib/debian/netdata.conf | 16 + contrib/debian/netdata.default | 5 + contrib/debian/netdata.docs | 1 + contrib/debian/netdata.init | 56 + contrib/debian/netdata.install | 1 + contrib/debian/netdata.lintian-overrides | 16 + contrib/debian/netdata.postinst.in | 41 + contrib/debian/netdata.postrm | 43 + contrib/debian/netdata.service | 14 + contrib/debian/rules | 87 + contrib/debian/source/format | 1 + contrib/nc-backend.sh | 151 ++ coverity-scan.sh | 5 +- depcomp | 487 +++--- diagrams/netdata-overview.xml | 1 + install-sh | 14 +- installer/functions.sh | 340 +++- kickstart-static64.sh | 232 +++ kickstart.sh | 374 +++++ m4/ax_c___atomic.m4 | 6 + makeself/build-x86_64-static.sh | 39 + makeself/build.sh | 38 + makeself/functions.sh | 59 + makeself/install-or-update.sh | 162 ++ makeself/jobs/10-prepare-destination.install.sh | 16 + makeself/jobs/50-bash-4.4.install.sh | 47 + makeself/jobs/50-curl-7.53.1.install.sh | 30 + makeself/jobs/50-fping-4.0.install.sh | 25 + makeself/jobs/70-netdata-git.install.sh | 16 + makeself/jobs/99-makeself.install.sh | 121 ++ makeself/makeself-header.sh | 554 ++++++ makeself/makeself-help-header.txt | 46 + makeself/makeself-license.txt | 46 + makeself/makeself.lsm | 16 + makeself/makeself.sh | 620 +++++++ makeself/post-installer.sh | 10 + makeself/run-all-jobs.sh | 46 + makeself/setup-x86_64-static.sh | 26 + missing | 414 ++--- netdata-installer.sh | 356 ++-- netdata.spec | 25 +- netdata.spec.in | 21 +- node.d/Makefile.am | 1 + node.d/Makefile.in | 124 +- node.d/README.md | 63 + node.d/fronius.node.js | 317 ++++ node.d/node_modules/net-snmp.js | 63 +- plugins.d/Makefile.in | 117 +- plugins.d/alarm-notify.sh | 147 +- plugins.d/alarm-test.sh | 2 +- plugins.d/cgroup-name.sh | 48 +- plugins.d/charts.d.plugin | 9 +- plugins.d/fping.plugin | 26 +- plugins.d/node.d.plugin | 6 +- plugins.d/python.d.plugin | 19 +- plugins.d/tc-qos-helper.sh | 9 +- python.d/Makefile.am | 4 + python.d/Makefile.in | 144 +- python.d/README.md | 170 ++ python.d/apache.chart.py | 72 +- python.d/bind_rndc.chart.py | 319 ++-- python.d/cpufreq.chart.py | 2 +- python.d/dns_query_time.chart.py | 135 ++ python.d/elasticsearch.chart.py | 198 +-- python.d/fail2ban.chart.py | 267 +-- python.d/go_expvar.chart.py | 228 +++ python.d/haproxy.chart.py | 254 +-- python.d/isc_dhcpd.chart.py | 190 +-- python.d/mdstat.chart.py | 167 +- python.d/mongodb.chart.py | 53 +- python.d/mysql.chart.py | 10 +- python.d/ovpn_status_log.chart.py | 94 +- python.d/postgres.chart.py | 12 +- python.d/python_modules/base.py | 211 ++- python.d/rabbitmq.chart.py | 187 +++ python.d/redis.chart.py | 5 +- python.d/samba.chart.py | 124 ++ python.d/smartd_log.chart.py | 13 +- python.d/web_log.chart.py | 1179 ++++++++----- src/Makefile.am | 16 + src/Makefile.in | 315 +++- src/appconfig.c | 55 +- src/appconfig.h | 7 +- src/apps_plugin.c | 213 ++- src/backend_prometheus.c | 397 +++++ src/backend_prometheus.h | 11 + src/backends.c | 311 +++- src/backends.h | 26 +- src/clocks.c | 4 +- src/clocks.h | 2 + src/common.c | 266 ++- src/common.h | 12 +- src/daemon.c | 56 +- src/eval.c | 2 +- src/freebsd_devstat.c | 662 ++++++++ src/freebsd_getifaddrs.c | 494 ++++++ src/freebsd_getmntinfo.c | 293 ++++ src/freebsd_ipfw.c | 360 ++++ src/freebsd_kstat_zfs.c | 212 +++ src/freebsd_sysctl.c | 1283 +------------- src/freeipmi_plugin.c | 13 +- src/health.c | 15 +- src/health_config.c | 55 +- src/inlined.h | 97 ++ src/log.c | 176 +- src/log.h | 12 +- src/main.c | 100 +- src/plugin_freebsd.c | 6 + src/plugin_freebsd.h | 8 + src/plugin_idlejitter.c | 72 +- src/plugin_proc.c | 5 +- src/plugin_proc.h | 4 + src/plugin_proc_diskspace.c | 41 +- src/plugin_tc.c | 6 +- src/plugins_d.c | 99 +- src/plugins_d.h | 10 + src/proc_diskstats.c | 173 +- src/proc_loadavg.c | 5 +- src/proc_net_dev.c | 14 +- src/proc_net_netstat.c | 8 + src/proc_net_snmp.c | 37 +- src/proc_net_snmp6.c | 4 +- src/proc_net_softnet_stat.c | 4 +- src/proc_spl_kstat_zfs.c | 153 ++ src/proc_vmstat.c | 20 +- src/registry.c | 2 +- src/registry.h | 2 + src/registry_init.c | 2 +- src/registry_internals.c | 4 + src/rrd.c | 11 +- src/rrd.h | 98 +- src/rrd2json.c | 83 +- src/rrd2json.h | 24 +- src/rrd2json_api_old.c | 4 +- src/rrdcalc.c | 2 +- src/rrdcalctemplate.c | 2 +- src/rrddim.c | 175 +- src/rrdhost.c | 154 +- src/rrdpush.c | 249 ++- src/rrdpush.h | 2 + src/rrdset.c | 921 ++++++---- src/socket.c | 1006 ++++++++++- src/socket.h | 55 +- src/statistical.c | 459 +++++ src/statistical.h | 19 + src/statsd.c | 2041 +++++++++++++++++++++++ src/statsd.h | 9 + src/storage_number.h | 1 + src/sys_fs_cgroup.c | 48 +- src/unit_test.c | 56 +- src/unit_test.h | 1 + src/web_api_v1.c | 42 +- src/web_client.c | 54 +- src/web_client.h | 1 - src/web_server.c | 366 +--- src/web_server.h | 26 +- src/zfs_common.c | 677 ++++++++ src/zfs_common.h | 109 ++ system/Makefile.in | 102 +- web/Makefile.am | 1 + web/Makefile.in | 145 +- web/dashboard.css | 47 +- web/dashboard.html | 2 +- web/dashboard.js | 1899 ++++++++++++--------- web/dashboard.slate.css | 47 +- web/dashboard_info.js | 391 ++++- web/index.html | 186 ++- web/infographic.html | 170 ++ web/version.txt | 2 +- 221 files changed, 23143 insertions(+), 7148 deletions(-) create mode 100644 LICENSE create mode 100644 LICENSE-REDISTRIBUTED.md create mode 100755 compile create mode 100644 conf.d/health.d/lighttpd.conf create mode 100644 conf.d/health.d/mongodb.conf create mode 100644 conf.d/health.d/zfs.conf create mode 100644 conf.d/node.d/fronius.conf.md create mode 100644 conf.d/python.d/dns_query_time.conf create mode 100644 conf.d/python.d/go_expvar.conf create mode 100644 conf.d/python.d/rabbitmq.conf create mode 100644 conf.d/python.d/samba.conf create mode 100644 conf.d/statsd.d/example.conf create mode 100644 contrib/debian/changelog create mode 100644 contrib/debian/compat create mode 100644 contrib/debian/control create mode 100644 contrib/debian/control.wheezy create mode 100644 contrib/debian/copyright create mode 100644 contrib/debian/netdata.conf create mode 100644 contrib/debian/netdata.default create mode 100644 contrib/debian/netdata.docs create mode 100755 contrib/debian/netdata.init create mode 100644 contrib/debian/netdata.install create mode 100644 contrib/debian/netdata.lintian-overrides create mode 100644 contrib/debian/netdata.postinst.in create mode 100644 contrib/debian/netdata.postrm create mode 100644 contrib/debian/netdata.service create mode 100755 contrib/debian/rules create mode 100644 contrib/debian/source/format create mode 100755 contrib/nc-backend.sh create mode 100644 diagrams/netdata-overview.xml create mode 100755 kickstart-static64.sh create mode 100755 kickstart.sh create mode 100755 makeself/build-x86_64-static.sh create mode 100755 makeself/build.sh create mode 100755 makeself/functions.sh create mode 100755 makeself/install-or-update.sh create mode 100755 makeself/jobs/10-prepare-destination.install.sh create mode 100755 makeself/jobs/50-bash-4.4.install.sh create mode 100755 makeself/jobs/50-curl-7.53.1.install.sh create mode 100755 makeself/jobs/50-fping-4.0.install.sh create mode 100755 makeself/jobs/70-netdata-git.install.sh create mode 100755 makeself/jobs/99-makeself.install.sh create mode 100755 makeself/makeself-header.sh create mode 100644 makeself/makeself-help-header.txt create mode 100644 makeself/makeself-license.txt create mode 100644 makeself/makeself.lsm create mode 100755 makeself/makeself.sh create mode 100755 makeself/post-installer.sh create mode 100755 makeself/run-all-jobs.sh create mode 100755 makeself/setup-x86_64-static.sh create mode 100644 node.d/fronius.node.js create mode 100644 python.d/dns_query_time.chart.py create mode 100644 python.d/go_expvar.chart.py create mode 100644 python.d/rabbitmq.chart.py create mode 100644 python.d/samba.chart.py create mode 100644 src/backend_prometheus.c create mode 100644 src/backend_prometheus.h create mode 100644 src/freebsd_devstat.c create mode 100644 src/freebsd_getifaddrs.c create mode 100644 src/freebsd_getmntinfo.c create mode 100644 src/freebsd_ipfw.c create mode 100644 src/freebsd_kstat_zfs.c create mode 100644 src/proc_spl_kstat_zfs.c create mode 100644 src/statistical.c create mode 100644 src/statistical.h create mode 100644 src/statsd.c create mode 100644 src/statsd.h create mode 100644 src/zfs_common.c create mode 100644 src/zfs_common.h create mode 100644 web/infographic.html diff --git a/.gitignore b/.gitignore index 0169e9316..00c7d6d68 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,8 @@ netdata.spec *.tar.* +cmake-build-debug/ +makeself/tmp/ sitespeed-result/ cov-int/ netdata-coverity-analysis.tgz @@ -93,6 +95,7 @@ profile/benchmark-dictionary profile/benchmark-registry *.pyc +*.run diagrams/*.png diagrams/*.svg diff --git a/.travis/deploy-if-have-key b/.travis/deploy-if-have-key index 50e69b939..1933eeb2e 100755 --- a/.travis/deploy-if-have-key +++ b/.travis/deploy-if-have-key @@ -32,6 +32,12 @@ then exit 0 fi +if [ "$TRAVIS_OS_NAME" != "linux" ] +then + echo "Building non-linux version - skipping deployment to website" + exit 0 +fi + if [ "$CC" != "gcc" ] then echo "Building non-gcc version - skipping deployment to website" @@ -41,4 +47,5 @@ fi ssh-keyscan -H firehol.org >> ~/.ssh/known_hosts ssh travis@firehol.org mkdir -p uploads/netdata/$TRAVIS_BRANCH/ scp -p *.tar.* travis@firehol.org:uploads/netdata/$TRAVIS_BRANCH/ +scp -p *.gz.run* travis@firehol.org:uploads/netdata/$TRAVIS_BRANCH/ ssh travis@firehol.org touch uploads/netdata/$TRAVIS_BRANCH/complete.txt diff --git a/ChangeLog b/ChangeLog index 1f6c5f274..d3c20b804 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,55 @@ +netdata (1.7.0) - 2017-07-16 + + * netdata is still spreading fast + + we are at 320.000 users and 132.000 servers + + Almost 100k new users, 52k new installations and 800k docker pulls + since the previous release, 4 and a half months ago. + + netdata user base grows at about 1000 new users and 600 new servers + per day. Thank you. You are awesome. + + * The next release (v1.8) will be focused on providing a global health + monitoring service, for all netdata users, for free. + + * netdata is now a (very fast) fully featured statsd server and the + only one with automatic visualization: push a statsd metric and hit + F5 on the netdata dashboard: your metric visualized. It also supports + synthetic charts, defined by you, so that you can correlate and + visualize your application the way you like it. + + * netdata got new installation options + It is now easier than ever to install netdata - we also distribute a + statically linked netdata x86_64 binary, including key dependencies + (like bash, curl, etc) that can run everywhere a Linux kernel runs + (CoreOS, CirrOS, etc). + + * metrics streaming and replication has been improved significantly. + All known issues have been solved and key enhancements have been added. + Headless collectors and proxies can now send metrics to backends when + data source = as collected. + + * backends have got quite a few enhancements, including host tags and + metrics filtering at the netdata side; + prometheus support has been re-written to utilize more prometheus + features and provide more flexibility and integration options. + + * netdata now monitors ZFS (on Linux and FreeBSD), ElasticSearch, + RabbitMQ, Go applications (via expvar), ipfw (on FreeBSD 11), samba, + squid logs (with web_log plugin). + + * netdata dashboard loading times have been improved significantly + (hit F5 a few times on a netdata dashboard - it is now amazingly fast), + to support dashboards with thousands of charts. + + * netdata alarms now support custom hooks, so you can run whatever you + like in parallel with netdata alarms. + + * As usual, this release brings dozens of more improvements, enhancements + and compatibility fixes. + + netdata (1.6.0) - 2017-03-20 * birthday release: 1 year netdata diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..9cecc1d46 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE-REDISTRIBUTED.md b/LICENSE-REDISTRIBUTED.md new file mode 100644 index 000000000..79891e5a3 --- /dev/null +++ b/LICENSE-REDISTRIBUTED.md @@ -0,0 +1,150 @@ +# Netdata + +Copyright 2016-2017, Costa Tsaousis. +Released under [GPL v3 or later](http://www.gnu.org/licenses/gpl-3.0.en.html). + +--- + +## Re-distributed software + +Netdata re-distributes the following third party software. +We decided to re-distribute all these, instead of using them +through a CDN, to allow netdata work in cases where internet +connectivity is not available. + + +- [Dygraphs](http://dygraphs.com/) + + Copyright 2009, Dan Vanderkam + [MIT License](http://dygraphs.com/legal.html) + + +- [jQuery Sparklines](http://omnipotent.net/jquery.sparkline/) + + Copyright 2009-2012, Splunk Inc. + [New BSD License](http://opensource.org/licenses/BSD-3-Clause) + + +- [Peity](http://benpickles.github.io/peity/) + + Copyright 2009-2015, Ben Pickles + [MIT License](https://github.com/benpickles/peity/blob/master/MIT-LICENCE) + + +- [Easy Pie Chart](https://rendro.github.io/easy-pie-chart/) + + Copyright 2013, Robert Fleischmann + [MIT License](https://github.com/rendro/easy-pie-chart/blob/master/LICENSE) + + +- [Guage.js](http://bernii.github.io/gauge.js/) + + Copyright, Bernard Kobos + [MIT License](http://bernii.github.io/gauge.js/) + + +- [jQuery](https://jquery.org/) + + Copyright 2015, jQuery Foundation + [MIT License](https://jquery.org/license/) + + +- [Bootstrap](http://getbootstrap.com/getting-started/) + + Copyright 2015, Twitter + [MIT License](http://getbootstrap.com/getting-started/#license-faqs) + + +- [Bootstrap Toggle](http://www.bootstraptoggle.com/) + + Copyright (c) 2011-2014 Min Hur, The New York Times Company + [MIT License](https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE) + + +- [bootstrap-table](http://bootstrap-table.wenzhixin.net.cn/) + + Copyright (c) 2012-2016 Zhixin Wen + [MIT License](https://github.com/wenzhixin/bootstrap-table/blob/master/LICENSE) + + +- [tableExport.jquery.plugin](https://github.com/hhurz/tableExport.jquery.plugin) + + Copyright (c) 2015,2016 hhurz + [MIT License](http://rawgit.com/hhurz/tableExport.jquery.plugin/master/tableExport.js) + + +- [perfect-scrollbar](https://jamesflorentino.github.io/nanoScrollerJS/) + + Copyright 2016, Hyunje Alex Jun and other contributors + [MIT License](https://github.com/noraesae/perfect-scrollbar/blob/master/LICENSE) + + +- [FontAwesome](https://fortawesome.github.io/Font-Awesome/) + + Created by Dave Gandy + Font license: [SIL OFL 1.1](http://scripts.sil.org/OFL) + CSS license: [MIT License](http://opensource.org/licenses/mit-license.html) + + +- [IconsDB.com Icons](http://www.iconsdb.com/soylent-red-icons/seo-performance-icon.html) + + Icons provided as CC0 1.0 Universal (CC0 1.0) Public Domain Dedication + + +- [morris.js](http://morrisjs.github.io/morris.js/) + + Copyright 2013, Olly Smith + [Simplified BSD License](http://morrisjs.github.io/morris.js/) + + +- [Raphaël](http://raphaeljs.com/) + + Copyright 2008, Dmitry Baranovskiy + [MIT License](http://raphaeljs.com/license.html) + + +- [C3](http://c3js.org/) + + Copyright 2013, Masayuki Tanaka + [MIT License](https://github.com/masayuki0812/c3/blob/master/LICENSE) + + +- [D3](http://d3js.org/) + + Copyright 2015, Mike Bostock + [BSD License](http://opensource.org/licenses/BSD-3-Clause) + + +- [node-extend](https://github.com/justmoon/node-extend) + + Copyright 2014, Stefan Thomas + [MIT License](https://github.com/justmoon/node-extend/blob/master/LICENSE) + + +- [node-net-snmp](https://github.com/stephenwvickers/node-net-snmp) + + Copyright 2013, Stephen Vickers + [MIT License](https://github.com/stephenwvickers/node-net-snmp) + + +- [node-asn1](https://github.com/mcavage/node-asn1) + + Copyright 2011, Mark Cavage + [MIT License](https://github.com/mcavage/node-asn1) + + +- [pixl-xml](https://github.com/jhuckaby/pixl-xml) + + Copyright 2015, Joseph Huckaby + [MIT License](https://github.com/jhuckaby/pixl-xml) + +- [sensors](https://github.com/paroj/sensors.py) + + Copyright 2014, Pavel Rojtberg + [LGPL 2.1 License](http://opensource.org/licenses/LGPL-2.1) + +- [PyYAML](https://bitbucket.org/blackjack/pysensors) + + Copyright 2006, Kirill Simonov + [MIT License](http://pyyaml.org) + diff --git a/LICENSE.md b/LICENSE.md index 79891e5a3..37a09f485 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,150 +1,9 @@ -# Netdata +**netdata**
    +(C) Copyright 2017
    +Costa Tsaousis <costa@tsaousis.gr> -Copyright 2016-2017, Costa Tsaousis. -Released under [GPL v3 or later](http://www.gnu.org/licenses/gpl-3.0.en.html). +For license details refer to the following files: ---- - -## Re-distributed software - -Netdata re-distributes the following third party software. -We decided to re-distribute all these, instead of using them -through a CDN, to allow netdata work in cases where internet -connectivity is not available. - - -- [Dygraphs](http://dygraphs.com/) - - Copyright 2009, Dan Vanderkam - [MIT License](http://dygraphs.com/legal.html) - - -- [jQuery Sparklines](http://omnipotent.net/jquery.sparkline/) - - Copyright 2009-2012, Splunk Inc. - [New BSD License](http://opensource.org/licenses/BSD-3-Clause) - - -- [Peity](http://benpickles.github.io/peity/) - - Copyright 2009-2015, Ben Pickles - [MIT License](https://github.com/benpickles/peity/blob/master/MIT-LICENCE) - - -- [Easy Pie Chart](https://rendro.github.io/easy-pie-chart/) - - Copyright 2013, Robert Fleischmann - [MIT License](https://github.com/rendro/easy-pie-chart/blob/master/LICENSE) - - -- [Guage.js](http://bernii.github.io/gauge.js/) - - Copyright, Bernard Kobos - [MIT License](http://bernii.github.io/gauge.js/) - - -- [jQuery](https://jquery.org/) - - Copyright 2015, jQuery Foundation - [MIT License](https://jquery.org/license/) - - -- [Bootstrap](http://getbootstrap.com/getting-started/) - - Copyright 2015, Twitter - [MIT License](http://getbootstrap.com/getting-started/#license-faqs) - - -- [Bootstrap Toggle](http://www.bootstraptoggle.com/) - - Copyright (c) 2011-2014 Min Hur, The New York Times Company - [MIT License](https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE) - - -- [bootstrap-table](http://bootstrap-table.wenzhixin.net.cn/) - - Copyright (c) 2012-2016 Zhixin Wen - [MIT License](https://github.com/wenzhixin/bootstrap-table/blob/master/LICENSE) - - -- [tableExport.jquery.plugin](https://github.com/hhurz/tableExport.jquery.plugin) - - Copyright (c) 2015,2016 hhurz - [MIT License](http://rawgit.com/hhurz/tableExport.jquery.plugin/master/tableExport.js) - - -- [perfect-scrollbar](https://jamesflorentino.github.io/nanoScrollerJS/) - - Copyright 2016, Hyunje Alex Jun and other contributors - [MIT License](https://github.com/noraesae/perfect-scrollbar/blob/master/LICENSE) - - -- [FontAwesome](https://fortawesome.github.io/Font-Awesome/) - - Created by Dave Gandy - Font license: [SIL OFL 1.1](http://scripts.sil.org/OFL) - CSS license: [MIT License](http://opensource.org/licenses/mit-license.html) - - -- [IconsDB.com Icons](http://www.iconsdb.com/soylent-red-icons/seo-performance-icon.html) - - Icons provided as CC0 1.0 Universal (CC0 1.0) Public Domain Dedication - - -- [morris.js](http://morrisjs.github.io/morris.js/) - - Copyright 2013, Olly Smith - [Simplified BSD License](http://morrisjs.github.io/morris.js/) - - -- [Raphaël](http://raphaeljs.com/) - - Copyright 2008, Dmitry Baranovskiy - [MIT License](http://raphaeljs.com/license.html) - - -- [C3](http://c3js.org/) - - Copyright 2013, Masayuki Tanaka - [MIT License](https://github.com/masayuki0812/c3/blob/master/LICENSE) - - -- [D3](http://d3js.org/) - - Copyright 2015, Mike Bostock - [BSD License](http://opensource.org/licenses/BSD-3-Clause) - - -- [node-extend](https://github.com/justmoon/node-extend) - - Copyright 2014, Stefan Thomas - [MIT License](https://github.com/justmoon/node-extend/blob/master/LICENSE) - - -- [node-net-snmp](https://github.com/stephenwvickers/node-net-snmp) - - Copyright 2013, Stephen Vickers - [MIT License](https://github.com/stephenwvickers/node-net-snmp) - - -- [node-asn1](https://github.com/mcavage/node-asn1) - - Copyright 2011, Mark Cavage - [MIT License](https://github.com/mcavage/node-asn1) - - -- [pixl-xml](https://github.com/jhuckaby/pixl-xml) - - Copyright 2015, Joseph Huckaby - [MIT License](https://github.com/jhuckaby/pixl-xml) - -- [sensors](https://github.com/paroj/sensors.py) - - Copyright 2014, Pavel Rojtberg - [LGPL 2.1 License](http://opensource.org/licenses/LGPL-2.1) - -- [PyYAML](https://bitbucket.org/blackjack/pysensors) - - Copyright 2006, Kirill Simonov - [MIT License](http://pyyaml.org) +- [netdata license](LICENSE) (GPL v3+) +- [third party licenses](LICENSE-REDISTRIBUTED.md), for packages re-distributed with netdata diff --git a/Makefile.am b/Makefile.am index 3ccf82f8f..7d5bc57f8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,7 +37,9 @@ EXTRA_DIST = \ m4/ax_c__generic.m4 \ autogen.sh \ README.md \ + LICENSE \ LICENSE.md \ + LICENSE-REDISTRIBUTED.md \ COPYING \ autogen.sh \ tests/stress.sh \ @@ -60,6 +62,7 @@ dist_noinst_DATA= \ diagrams/registry.puml \ diagrams/netdata-for-ephemeral-nodes.xml \ diagrams/netdata-proxies-example.xml \ + diagrams/netdata-overview.xml \ configs.signatures \ Dockerfile \ netdata.spec \ @@ -71,6 +74,26 @@ dist_noinst_SCRIPTS= \ diagrams/build.sh \ coverity-scan.sh \ docker-build.sh \ + kickstart.sh \ + kickstart-static64.sh \ netdata-installer.sh \ installer/functions.sh \ + makeself/build.sh \ + makeself/makeself.sh \ + makeself/makeself-license.txt \ + makeself/setup-x86_64-static.sh \ + makeself/post-installer.sh \ + makeself/jobs/10-prepare-destination.install.sh \ + makeself/jobs/50-curl-7.53.1.install.sh \ + makeself/jobs/50-bash-4.4.install.sh \ + makeself/jobs/50-fping-4.0.install.sh \ + makeself/jobs/70-netdata-git.install.sh \ + makeself/jobs/99-makeself.install.sh \ + makeself/run-all-jobs.sh \ + makeself/install-or-update.sh \ + makeself/build-x86_64-static.sh \ + makeself/makeself-header.sh \ + makeself/makeself-help-header.txt \ + makeself/makeself.lsm \ + makeself/functions.sh \ $(NULL) diff --git a/Makefile.in b/Makefile.in index 492376f5e..aae24649b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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. @@ -17,6 +16,51 @@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -36,11 +80,11 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = . -DIST_COMMON = $(am__configure_deps) $(dist_noinst_DATA) \ - $(dist_noinst_SCRIPTS) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in $(srcdir)/config.h.in \ - $(srcdir)/netdata.spec.in $(top_srcdir)/configure COPYING \ - ChangeLog config.guess config.sub depcomp install-sh missing +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/configure $(am__configure_deps) \ + $(srcdir)/config.h.in $(srcdir)/netdata.spec.in \ + $(dist_noinst_SCRIPTS) $(dist_noinst_DATA) COPYING ChangeLog \ + compile config.guess config.sub install-sh missing 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_lto.m4 \ @@ -59,23 +103,63 @@ CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = netdata.spec CONFIG_CLEAN_VPATH_FILES = SCRIPTS = $(dist_noinst_SCRIPTS) +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 = SOURCES = DIST_SOURCES = -RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ - html-recursive info-recursive install-data-recursive \ - install-dvi-recursive install-exec-recursive \ - install-html-recursive install-info-recursive \ - install-pdf-recursive install-ps-recursive install-recursive \ - installcheck-recursive installdirs-recursive pdf-recursive \ - ps-recursive uninstall-recursive +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac DATA = $(dist_noinst_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive -AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ - $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ - distdir dist dist-all distcheck +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope distdir dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)config.h.in +# 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 +CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) @@ -86,6 +170,7 @@ am__remove_distdir = \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi +am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ @@ -113,12 +198,14 @@ am__relativize = \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz $(distdir).tar.bz2 $(distdir).tar.xz GZIP_ENV = --best +DIST_TARGETS = dist-xz dist-bzip2 dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -299,7 +386,9 @@ EXTRA_DIST = \ m4/ax_c__generic.m4 \ autogen.sh \ README.md \ + LICENSE \ LICENSE.md \ + LICENSE-REDISTRIBUTED.md \ COPYING \ autogen.sh \ tests/stress.sh \ @@ -322,6 +411,7 @@ dist_noinst_DATA = \ diagrams/registry.puml \ diagrams/netdata-for-ephemeral-nodes.xml \ diagrams/netdata-proxies-example.xml \ + diagrams/netdata-overview.xml \ configs.signatures \ Dockerfile \ netdata.spec \ @@ -334,8 +424,28 @@ dist_noinst_SCRIPTS = \ diagrams/build.sh \ coverity-scan.sh \ docker-build.sh \ + kickstart.sh \ + kickstart-static64.sh \ netdata-installer.sh \ installer/functions.sh \ + makeself/build.sh \ + makeself/makeself.sh \ + makeself/makeself-license.txt \ + makeself/setup-x86_64-static.sh \ + makeself/post-installer.sh \ + makeself/jobs/10-prepare-destination.install.sh \ + makeself/jobs/50-curl-7.53.1.install.sh \ + makeself/jobs/50-bash-4.4.install.sh \ + makeself/jobs/50-fping-4.0.install.sh \ + makeself/jobs/70-netdata-git.install.sh \ + makeself/jobs/99-makeself.install.sh \ + makeself/run-all-jobs.sh \ + makeself/install-or-update.sh \ + makeself/build-x86_64-static.sh \ + makeself/makeself-header.sh \ + makeself/makeself-help-header.txt \ + makeself/makeself.lsm \ + makeself/functions.sh \ $(NULL) all: config.h @@ -378,8 +488,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): config.h: stamp-h1 - @if test ! -f $@; then rm -f stamp-h1; else :; fi - @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) stamp-h1; else :; fi + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 @@ -395,22 +505,25 @@ netdata.spec: $(top_builddir)/config.status $(srcdir)/netdata.spec.in cd $(top_builddir) && $(SHELL) ./config.status $@ # This directory's subdirectories are mostly independent; you can cd -# into them and run `make' without going through this Makefile. -# To change the values of `make' variables: instead of editing Makefiles, -# (1) if the variable is set in `config.status', edit `config.status' -# (which will cause the Makefiles to be regenerated when you run `make'); -# (2) otherwise, pass the desired values on the `make' command line. -$(RECURSIVE_TARGETS): - @fail= failcom='exit 1'; \ - for f in x $$MAKEFLAGS; do \ - case $$f in \ - *=* | --[!k]*);; \ - *k*) failcom='fail=yes';; \ - esac; \ - done; \ +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ - list='$(SUBDIRS)'; for subdir in $$list; do \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ @@ -425,57 +538,12 @@ $(RECURSIVE_TARGETS): $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" -$(RECURSIVE_CLEAN_TARGETS): - @fail= failcom='exit 1'; \ - for f in x $$MAKEFLAGS; do \ - case $$f in \ - *=* | --[!k]*);; \ - *k*) failcom='fail=yes';; \ - esac; \ - done; \ - dot_seen=no; \ - case "$@" in \ - distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ - *) list='$(SUBDIRS)' ;; \ - esac; \ - rev=''; for subdir in $$list; do \ - if test "$$subdir" = "."; then :; else \ - rev="$$subdir $$rev"; \ - fi; \ - done; \ - rev="$$rev ."; \ - target=`echo $@ | sed s/-recursive//`; \ - for subdir in $$rev; do \ - echo "Making $$target in $$subdir"; \ - if test "$$subdir" = "."; then \ - local_target="$$target-am"; \ - else \ - local_target="$$target"; \ - fi; \ - ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ - || eval $$failcom; \ - done && test -z "$$fail" -tags-recursive: - list='$(SUBDIRS)'; for subdir in $$list; do \ - test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ - done -ctags-recursive: - list='$(SUBDIRS)'; for subdir in $$list; do \ - test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ - done +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags -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: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ @@ -491,12 +559,7 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ - list='$(SOURCES) $(HEADERS) config.h.in $(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; }; }'`; \ + $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ @@ -508,15 +571,11 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ $$unique; \ fi; \ fi -ctags: CTAGS -CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - list='$(SOURCES) $(HEADERS) config.h.in $(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; }; }'`; \ +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique @@ -525,9 +584,31 @@ GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +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 + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) @@ -563,13 +644,10 @@ distdir: $(DISTFILES) done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ - test -d "$(distdir)/$$subdir" \ - || $(MKDIR_P) "$(distdir)/$$subdir" \ - || exit 1; \ - fi; \ - done - @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ @@ -598,40 +676,40 @@ distdir: $(DISTFILES) || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz - $(am__remove_distdir) + $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 - $(am__remove_distdir) + $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz - $(am__remove_distdir) - -dist-lzma: distdir - tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma - $(am__remove_distdir) + $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz - $(am__remove_distdir) + $(am__post_remove_distdir) dist-tarZ: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z - $(am__remove_distdir) + $(am__post_remove_distdir) dist-shar: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz - $(am__remove_distdir) + $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) - $(am__remove_distdir) + $(am__post_remove_distdir) -dist dist-all: distdir - tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz - tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 - tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz - $(am__remove_distdir) +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another @@ -642,8 +720,6 @@ distcheck: dist GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ - *.tar.lzma*) \ - lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ @@ -655,18 +731,19 @@ distcheck: dist *.zip*) \ unzip $(distdir).zip ;;\ esac - chmod -R a-w $(distdir); chmod a+w $(distdir) - mkdir $(distdir)/_build - mkdir $(distdir)/_inst + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build \ - && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + && ../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ @@ -689,7 +766,7 @@ distcheck: dist && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 - $(am__remove_distdir) + $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' @@ -824,13 +901,12 @@ ps-am: uninstall-am: -.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all \ - ctags-recursive install-am install-strip tags-recursive +.MAKE: $(am__recursive_targets) all install-am install-strip -.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ - all all-am am--refresh check check-am clean clean-generic \ - ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \ - dist-lzip dist-lzma dist-shar dist-tarZ dist-xz dist-zip \ +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-am clean clean-cscope clean-generic \ + cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ + dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-generic distclean-hdr \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ @@ -840,8 +916,8 @@ uninstall-am: install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs installdirs-am \ maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic pdf pdf-am ps ps-am tags tags-recursive \ - uninstall uninstall-am + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/README.md b/README.md index cff7f31bd..15102b1d7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# netdata [![Build Status](https://travis-ci.org/firehol/netdata.svg?branch=master)](https://travis-ci.org/firehol/netdata) [![Coverity Scan Build Status](https://scan.coverity.com/projects/9140/badge.svg)](https://scan.coverity.com/projects/firehol-netdata) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a994873f30d045b9b4b83606c3eb3498)](https://www.codacy.com/app/netdata/netdata?utm_source=github.com&utm_medium=referral&utm_content=firehol/netdata&utm_campaign=Badge_Grade) [![Code Climate](https://codeclimate.com/github/firehol/netdata/badges/gpa.svg)](https://codeclimate.com/github/firehol/netdata) +# netdata [![Build Status](https://travis-ci.org/firehol/netdata.svg?branch=master)](https://travis-ci.org/firehol/netdata) [![Coverity Scan Build Status](https://scan.coverity.com/projects/9140/badge.svg)](https://scan.coverity.com/projects/firehol-netdata) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a994873f30d045b9b4b83606c3eb3498)](https://www.codacy.com/app/netdata/netdata?utm_source=github.com&utm_medium=referral&utm_content=firehol/netdata&utm_campaign=Badge_Grade) [![Code Climate](https://codeclimate.com/github/firehol/netdata/badges/gpa.svg)](https://codeclimate.com/github/firehol/netdata) [![license](https://img.shields.io/github/license/firehol/netdata.svg)](LICENSE) > *New to netdata? Here is a live demo: [http://my-netdata.io](http://my-netdata.io)* **netdata** is a system for **distributed real-time performance and health monitoring**. @@ -13,6 +13,7 @@ disrupting their core function._ netdata runs on **Linux**, **FreeBSD**, and **MacOS**. [![Twitter Follow](https://img.shields.io/twitter/follow/linuxnetdata.svg?style=social&label=New%20-%20stay%20in%20touch%20-%20follow%20netdata%20on%20twitter)](https://twitter.com/linuxnetdata) +[![analytics](http://www.google-analytics.com/collect?v=1&t=pageview&_s=1&ds=github&dr=https%3A%2F%2Fgithub.com%2Ffirehol%2Fnetdata&dl=https%3A%2F%2Fmy-netdata.io%2Fgithub%2Freadme&_u=MAC~&cid=5792dfd7-8dc4-476b-af31-da2fdb9f93d2&tid=UA-64295674-3)]() --- @@ -273,10 +274,22 @@ This is a list of what it currently monitors: - **SNMP devices**
    can be monitored too (although you will need to configure these) +- **statsd**
    + [netdata is a fully featured statsd server](https://github.com/firehol/netdata/wiki/statsd) + And you can extend it, by writing plugins that collect data from any source, using any computer language. --- +## netdata infographic + +This is a high level overview of netdata feature set and architecture. +Click it to to interact with it (it has direct links to documentation). + +[![netdata-overview](https://cloud.githubusercontent.com/assets/2662304/26529478/104652ac-43c9-11e7-903f-edb9bb2ced24.png)](https://my-netdata.io/infographic.html) + +--- + ## Installation Use our **[automatic installer](https://github.com/firehol/netdata/wiki/Installation)** to build and install it on your system. @@ -303,6 +316,6 @@ Check the **[netdata wiki](https://github.com/firehol/netdata/wiki)**. ## License -netdata is GPLv3+. +netdata is [GPLv3+](LICENSE). -It re-distributes other open-source tools and libraries. Please check its [License Statement](https://github.com/firehol/netdata/blob/master/LICENSE.md). +It re-distributes other open-source tools and libraries. Please check the [third party licenses](https://github.com/firehol/netdata/blob/master/LICENSE-REDISTRIBUTED.md). diff --git a/aclocal.m4 b/aclocal.m4 index 2bb8c79ed..58b64dc77 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,8 +1,7 @@ -# generated automatically by aclocal 1.11.3 -*- Autoconf -*- +# generated automatically by aclocal 1.14.1 -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, -# 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, -# Inc. # This file 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. @@ -12,13 +11,14 @@ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl -m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.68],, -[m4_warning([this file was generated for autoconf 2.68. +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. -To do so, use the procedure documented by the package, typically `autoreconf'.])]) +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 1 (pkg-config-0.24) @@ -180,25 +180,22 @@ else fi[]dnl ])# PKG_CHECK_MODULES -# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 2002-2013 Free Software Foundation, Inc. # # This file 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. -# serial 1 - # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.11' +[am__api_version='1.14' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.11.3], [], +m4_if([$1], [1.14.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -214,24 +211,22 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.11.3])dnl +[AM_AUTOMAKE_VERSION([1.14.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc. +# Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file 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. -# serial 1 - # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets -# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to -# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and @@ -250,7 +245,7 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually -# harmless because $srcdir is `.', but things will broke when you +# harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, @@ -276,22 +271,19 @@ am_aux_dir=`cd $ac_aux_dir && pwd` # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 -# Free Software Foundation, Inc. +# Copyright (C) 1997-2013 Free Software Foundation, Inc. # # This file 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. -# serial 9 - # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], -[AC_PREREQ(2.52)dnl - ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], - [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl @@ -310,16 +302,14 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, -# 2010, 2011 Free Software Foundation, Inc. +# Copyright (C) 1999-2013 Free Software Foundation, Inc. # # This file 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. -# serial 12 -# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing @@ -329,7 +319,7 @@ fi])]) # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. -# NAME is "CC", "CXX", "GCJ", or "OBJC". +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was @@ -342,12 +332,13 @@ AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl -ifelse([$1], CC, [depcc="$CC" am_compiler_list=], - [$1], CXX, [depcc="$CXX" am_compiler_list=], - [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], - [$1], UPC, [depcc="$UPC" am_compiler_list=], - [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], - [depcc="$$1" am_compiler_list=]) +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], @@ -355,8 +346,8 @@ AC_CACHE_CHECK([dependency style of $depcc], # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named `D' -- because `-MD' means `put the output - # in D'. + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're @@ -396,16 +387,16 @@ AC_CACHE_CHECK([dependency style of $depcc], : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with - # Solaris 8's {/usr,}/bin/sh. - touch sub/conftst$i.h + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - # We check with `-c' and `-o' for the sake of the "dashmstdout" + # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly - # handle `-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in @@ -414,8 +405,8 @@ AC_CACHE_CHECK([dependency style of $depcc], test "$am__universal" = false || continue ;; nosideeffect) - # after this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else @@ -423,7 +414,7 @@ AC_CACHE_CHECK([dependency style of $depcc], fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok `-c -o', but also, the minuso test has + # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} @@ -471,7 +462,7 @@ AM_CONDITIONAL([am__fastdep$1], [ # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. -# This macro is AC_REQUIREd in _AM_DEPENDENCIES +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl @@ -481,9 +472,13 @@ AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], -[AC_ARG_ENABLE(dependency-tracking, -[ --disable-dependency-tracking speeds up one-time build - --enable-dependency-tracking do not reject slow dependency extractors]) +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' @@ -498,20 +493,18 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 -# Free Software Foundation, Inc. +# Copyright (C) 1999-2013 Free Software Foundation, Inc. # # This file 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. -#serial 5 # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ - # Autoconf 2.62 quotes --file arguments for eval, but not when files + # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in @@ -524,7 +517,7 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named `Makefile.in', but + # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. @@ -536,21 +529,19 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], continue fi # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running `make'. + # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "am__include" && continue + test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # When using ansi2knr, U may be empty or an underscore; expand it - U=`sed -n 's/^U = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` @@ -568,7 +559,7 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking -# is enabled. FIXME. This creates each `.P' file that we will +# is enabled. FIXME. This creates each '.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], @@ -578,18 +569,21 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, -# 2005, 2006, 2008, 2009 Free Software Foundation, Inc. +# Copyright (C) 1996-2013 Free Software Foundation, Inc. # # This file 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. -# serial 16 - # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- @@ -602,7 +596,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], -[AC_PREREQ([2.62])dnl +[AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl @@ -631,31 +625,40 @@ AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], -[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. -m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, -[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) - AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl -AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) -AM_MISSING_PROG(AUTOCONF, autoconf) -AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) -AM_MISSING_PROG(AUTOHEADER, autoheader) -AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl -AC_REQUIRE([AM_PROG_MKDIR_P])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target. The system "awk" is bad on # some platforms. AC_REQUIRE([AC_PROG_AWK])dnl @@ -666,34 +669,78 @@ _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], - [_AM_DEPENDENCIES(CC)], - [define([AC_PROG_CC], - defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], - [_AM_DEPENDENCIES(CXX)], - [define([AC_PROG_CXX], - defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], - [_AM_DEPENDENCIES(OBJC)], - [define([AC_PROG_OBJC], - defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) -_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl -dnl The `parallel-tests' driver may need to know about EXEEXT, so add the -dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro -dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl -]) -dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) - # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. @@ -715,15 +762,12 @@ for _am_header in $config_headers :; do done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001, 2003, 2005, 2008, 2011 Free Software Foundation, -# Inc. +# Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file 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. -# serial 1 - # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. @@ -737,16 +781,14 @@ if test x"${install_sh}" != xset; then install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi -AC_SUBST(install_sh)]) +AC_SUBST([install_sh])]) -# Copyright (C) 2003, 2005 Free Software Foundation, Inc. +# Copyright (C) 2003-2013 Free Software Foundation, Inc. # # This file 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. -# serial 2 - # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], @@ -763,20 +805,17 @@ AC_SUBST([am__leading_dot])]) # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering -# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008, -# 2011 Free Software Foundation, Inc. +# Copyright (C) 1996-2013 Free Software Foundation, Inc. # # This file 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. -# serial 5 - # AM_MAINTAINER_MODE([DEFAULT-MODE]) # ---------------------------------- # Control maintainer-specific portions of Makefiles. -# Default is to disable them, unless `enable' is passed literally. -# For symmetry, `disable' may be passed as well. Anyway, the user +# Default is to disable them, unless 'enable' is passed literally. +# For symmetry, 'disable' may be passed as well. Anyway, the user # can override the default with the --enable/--disable switch. AC_DEFUN([AM_MAINTAINER_MODE], [m4_case(m4_default([$1], [disable]), @@ -787,10 +826,11 @@ AC_DEFUN([AM_MAINTAINER_MODE], AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) dnl maintainer-mode's default is 'disable' unless 'enable' is passed AC_ARG_ENABLE([maintainer-mode], -[ --][am_maintainer_other][-maintainer-mode am_maintainer_other make rules and dependencies not useful - (and sometimes confusing) to the casual installer], - [USE_MAINTAINER_MODE=$enableval], - [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) + [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], + am_maintainer_other[ make rules and dependencies not useful + (and sometimes confusing) to the casual installer])], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) AC_MSG_RESULT([$USE_MAINTAINER_MODE]) AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) MAINT=$MAINTAINER_MODE_TRUE @@ -798,18 +838,14 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) ] ) -AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) - # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. +# Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file 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. -# serial 4 - # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. @@ -827,7 +863,7 @@ am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf -# Ignore all kinds of additional output from `make'. +# Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include @@ -854,15 +890,12 @@ rm -f confinc confmf # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 -# Free Software Foundation, Inc. +# Copyright (C) 1997-2013 Free Software Foundation, Inc. # # This file 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. -# serial 6 - # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], @@ -870,11 +903,10 @@ AC_DEFUN([AM_MISSING_PROG], $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) - # AM_MISSING_HAS_RUN # ------------------ -# Define MISSING if not defined so far and test if it supports --run. -# If it does, set am_missing_run to use it, otherwise, to nothing. +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl @@ -887,54 +919,22 @@ if test x"${MISSING+set}" != xset; then esac fi # Use eval to expand $SHELL -if eval "$MISSING --run true"; then - am_missing_run="$MISSING --run " +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " else am_missing_run= - AC_MSG_WARN([`missing' script is too old or missing]) + AC_MSG_WARN(['missing' script is too old or missing]) fi ]) -# Copyright (C) 2003, 2004, 2005, 2006, 2011 Free Software Foundation, -# Inc. -# -# This file 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. - -# serial 1 - -# AM_PROG_MKDIR_P -# --------------- -# Check for `mkdir -p'. -AC_DEFUN([AM_PROG_MKDIR_P], -[AC_PREREQ([2.60])dnl -AC_REQUIRE([AC_PROG_MKDIR_P])dnl -dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, -dnl while keeping a definition of mkdir_p for backward compatibility. -dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. -dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of -dnl Makefile.ins that do not define MKDIR_P, so we do our own -dnl adjustment using top_builddir (which is defined more often than -dnl MKDIR_P). -AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl -case $mkdir_p in - [[\\/$]]* | ?:[[\\/]]*) ;; - */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; -esac -]) - # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001, 2002, 2003, 2005, 2008, 2010 Free Software -# Foundation, Inc. +# Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file 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. -# serial 5 - # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], @@ -944,7 +944,7 @@ AC_DEFUN([_AM_MANGLE_OPTION], # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], -[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ @@ -958,24 +958,82 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Check to make sure that the build environment is sane. -*- Autoconf -*- +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file 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. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 -# Free Software Foundation, Inc. +# Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file 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. -# serial 5 +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file 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. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) -# Just in case -sleep 1 -echo timestamp > conftest.file # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' @@ -986,32 +1044,40 @@ case `pwd` in esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) - AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac -# Do `set' in a subshell so we don't clobber the current shell's +# Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( - set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` - if test "$[*]" = "X"; then - # -L didn't work. - set X `ls -t "$srcdir/configure" conftest.file` - fi - rm -f conftest.file - if test "$[*]" != "X $srcdir/configure conftest.file" \ - && test "$[*]" != "X conftest.file $srcdir/configure"; then - - # If neither matched, then we have a broken ls. This can happen - # if, for instance, CONFIG_SHELL is bash and it inherits a - # broken ls alias from the environment. This has actually - # happened. Such a system could not be considered "sane". - AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken -alias in your environment]) - fi - + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done test "$[2]" = conftest.file ) then @@ -1021,46 +1087,118 @@ else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi -AC_MSG_RESULT(yes)]) +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) -# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc. +# Copyright (C) 2009-2013 Free Software Foundation, Inc. # # This file 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. -# serial 1 +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file 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. # AM_PROG_INSTALL_STRIP # --------------------- -# One issue with vendor `install' (even GNU) is that you can't +# One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we -# always use install-sh in `make install-strip', and initialize +# always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl -# Installed binaries are usually stripped using `strip' when the user -# run `make install-strip'. However `strip' might not be the right +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake -# will honor the `STRIP' environment variable to overrule this program. -dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006, 2008, 2010 Free Software Foundation, Inc. +# Copyright (C) 2006-2013 Free Software Foundation, Inc. # # This file 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. -# serial 3 - # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. @@ -1074,18 +1212,16 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004, 2005, 2012 Free Software Foundation, Inc. +# Copyright (C) 2004-2013 Free Software Foundation, Inc. # # This file 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. -# serial 2 - # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. -# FORMAT should be one of `v7', `ustar', or `pax'. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory @@ -1095,76 +1231,114 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar +# AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) -m4_if([$1], [v7], - [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], - [m4_case([$1], [ustar],, [pax],, - [m4_fatal([Unknown tar format])]) -AC_MSG_CHECKING([how to create a $1 tar archive]) -# Loop over all known methods to create a tar archive until one works. + +# We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' -_am_tools=${am_cv_prog_tar_$1-$_am_tools} -# Do not fold the above two line into one, because Tru64 sh and -# Solaris sh will not grok spaces in the rhs of `-'. -for _am_tool in $_am_tools -do - case $_am_tool in - gnutar) - for _am_tar in tar gnutar gtar; - do - AM_RUN_LOG([$_am_tar --version]) && break - done - am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' - am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' - am__untar="$_am_tar -xf -" - ;; - plaintar) - # Must skip GNU tar: if it does not support --format= it doesn't create - # ustar tarball either. - (tar --version) >/dev/null 2>&1 && continue - am__tar='tar chf - "$$tardir"' - am__tar_='tar chf - "$tardir"' - am__untar='tar xf -' - ;; - pax) - am__tar='pax -L -x $1 -w "$$tardir"' - am__tar_='pax -L -x $1 -w "$tardir"' - am__untar='pax -r' - ;; - cpio) - am__tar='find "$$tardir" -print | cpio -o -H $1 -L' - am__tar_='find "$tardir" -print | cpio -o -H $1 -L' - am__untar='cpio -i -H $1 -d' - ;; - none) - am__tar=false - am__tar_=false - am__untar=false - ;; - esac - # If the value was cached, stop now. We just wanted to have am__tar - # and am__untar set. - test -n "${am_cv_prog_tar_$1}" && break +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac - # tar/untar a dummy directory, and stop if the command works - rm -rf conftest.dir - mkdir conftest.dir - echo GrepMe > conftest.dir/file - AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done rm -rf conftest.dir - if test -s conftest.tar; then - AM_RUN_LOG([$am__untar /dev/null 2>&1 && break - fi -done -rm -rf conftest.dir -AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) -AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR diff --git a/charts.d/Makefile.in b/charts.d/Makefile.in index a613e1b31..5d17f4d2a 100644 --- a/charts.d/Makefile.in +++ b/charts.d/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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. @@ -17,6 +16,51 @@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -36,8 +80,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = charts.d -DIST_COMMON = $(dist_charts_DATA) $(dist_charts_SCRIPTS) \ - $(srcdir)/Makefile.am $(srcdir)/Makefile.in +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(dist_charts_SCRIPTS) $(dist_charts_DATA) 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_lto.m4 \ @@ -82,12 +126,31 @@ am__uninstall_files_from_dir = { \ } am__installdirs = "$(DESTDIR)$(chartsdir)" "$(DESTDIR)$(chartsdir)" SCRIPTS = $(dist_charts_SCRIPTS) +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 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac DATA = $(dist_charts_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -295,8 +358,11 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_chartsSCRIPTS: $(dist_charts_SCRIPTS) @$(NORMAL_INSTALL) - test -z "$(chartsdir)" || $(MKDIR_P) "$(DESTDIR)$(chartsdir)" @list='$(dist_charts_SCRIPTS)'; test -n "$(chartsdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(chartsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(chartsdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ @@ -327,8 +393,11 @@ uninstall-dist_chartsSCRIPTS: dir='$(DESTDIR)$(chartsdir)'; $(am__uninstall_files_from_dir) install-dist_chartsDATA: $(dist_charts_DATA) @$(NORMAL_INSTALL) - test -z "$(chartsdir)" || $(MKDIR_P) "$(DESTDIR)$(chartsdir)" @list='$(dist_charts_DATA)'; test -n "$(chartsdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(chartsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(chartsdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -343,11 +412,11 @@ uninstall-dist_chartsDATA: @list='$(dist_charts_DATA)'; test -n "$(chartsdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(chartsdir)'; $(am__uninstall_files_from_dir) -tags: TAGS -TAGS: +tags TAGS: + +ctags CTAGS: -ctags: CTAGS -CTAGS: +cscope cscopelist: distdir: $(DISTFILES) @@ -486,16 +555,17 @@ uninstall-am: uninstall-dist_chartsDATA uninstall-dist_chartsSCRIPTS .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-dist_chartsDATA install-dist_chartsSCRIPTS 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-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am \ +.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ + ctags-am distclean distclean-generic distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dist_chartsDATA \ + install-dist_chartsSCRIPTS 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-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am tags-am uninstall uninstall-am \ uninstall-dist_chartsDATA uninstall-dist_chartsSCRIPTS diff --git a/compile b/compile new file mode 100755 index 000000000..531136b06 --- /dev/null +++ b/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/conf.d/Makefile.am b/conf.d/Makefile.am index efe1f2a6e..4cbecb56a 100644 --- a/conf.d/Makefile.am +++ b/conf.d/Makefile.am @@ -17,6 +17,7 @@ dist_config_DATA = \ nodeconfigdir=$(configdir)/node.d dist_nodeconfig_DATA = \ node.d/README.md \ + node.d/fronius.conf.md \ node.d/named.conf.md \ node.d/sma_webbox.conf.md \ node.d/snmp.conf.md \ @@ -28,12 +29,14 @@ dist_pythonconfig_DATA = \ python.d/apache_cache.conf \ python.d/bind_rndc.conf \ python.d/cpufreq.conf \ + python.d/dns_query_time.conf \ python.d/dovecot.conf \ python.d/elasticsearch.conf \ python.d/example.conf \ python.d/exim.conf \ python.d/fail2ban.conf \ python.d/freeradius.conf \ + python.d/go_expvar.conf \ python.d/haproxy.conf \ python.d/hddtemp.conf \ python.d/ipfs.conf \ @@ -48,8 +51,10 @@ dist_pythonconfig_DATA = \ python.d/phpfpm.conf \ python.d/postfix.conf \ python.d/postgres.conf \ + python.d/rabbitmq.conf \ python.d/redis.conf \ python.d/retroshare.conf \ + python.d/samba.conf \ python.d/sensors.conf \ python.d/squid.conf \ python.d/smartd_log.conf \ @@ -70,10 +75,12 @@ dist_healthconfig_DATA = \ health.d/ipfs.conf \ health.d/ipmi.conf \ health.d/isc_dhcpd.conf \ + health.d/lighttpd.conf \ health.d/mdstat.conf \ health.d/memcached.conf \ health.d/mysql.conf \ health.d/named.conf \ + health.d/mongodb.conf \ health.d/nginx.conf \ health.d/postgres.conf \ health.d/redis.conf \ @@ -81,6 +88,7 @@ dist_healthconfig_DATA = \ health.d/squid.conf \ health.d/varnish.conf \ health.d/web_log.conf \ + health.d/zfs.conf \ $(NULL) if LINUX @@ -123,3 +131,8 @@ dist_chartsconfig_DATA = \ charts.d/postfix.conf \ charts.d/squid.conf \ $(NULL) + +statsdconfigdir=$(configdir)/statsd.d +dist_statsdconfig_DATA = \ + statsd.d/example.conf \ + $(NULL) diff --git a/conf.d/Makefile.in b/conf.d/Makefile.in index fb05396fb..7a1e300e0 100644 --- a/conf.d/Makefile.in +++ b/conf.d/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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,6 +15,51 @@ @SET_MAKE@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -51,10 +95,10 @@ host_triplet = @host@ @LINUX_TRUE@ $(NULL) subdir = conf.d -DIST_COMMON = $(am__dist_healthconfig_DATA_DIST) \ +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(dist_chartsconfig_DATA) $(dist_config_DATA) \ - $(dist_nodeconfig_DATA) $(dist_pythonconfig_DATA) \ - $(srcdir)/Makefile.am $(srcdir)/Makefile.in + $(am__dist_healthconfig_DATA_DIST) $(dist_nodeconfig_DATA) \ + $(dist_pythonconfig_DATA) $(dist_statsdconfig_DATA) 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_lto.m4 \ @@ -70,8 +114,25 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +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 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -101,27 +162,31 @@ am__uninstall_files_from_dir = { \ } am__installdirs = "$(DESTDIR)$(chartsconfigdir)" \ "$(DESTDIR)$(configdir)" "$(DESTDIR)$(healthconfigdir)" \ - "$(DESTDIR)$(nodeconfigdir)" "$(DESTDIR)$(pythonconfigdir)" + "$(DESTDIR)$(nodeconfigdir)" "$(DESTDIR)$(pythonconfigdir)" \ + "$(DESTDIR)$(statsdconfigdir)" am__dist_healthconfig_DATA_DIST = health.d/apache.conf \ health.d/backend.conf health.d/bind_rndc.conf \ health.d/elasticsearch.conf health.d/fping.conf \ health.d/haproxy.conf health.d/ipfs.conf health.d/ipmi.conf \ - health.d/isc_dhcpd.conf health.d/mdstat.conf \ - health.d/memcached.conf health.d/mysql.conf \ - health.d/named.conf health.d/nginx.conf health.d/postgres.conf \ - health.d/redis.conf health.d/retroshare.conf \ - health.d/squid.conf health.d/varnish.conf \ - health.d/web_log.conf health.d/cpu.conf health.d/disks.conf \ - health.d/entropy.conf health.d/ipc.conf health.d/memory.conf \ - health.d/net.conf health.d/netfilter.conf health.d/qos.conf \ - health.d/ram.conf health.d/softnet.conf health.d/swap.conf \ + health.d/isc_dhcpd.conf health.d/lighttpd.conf \ + health.d/mdstat.conf health.d/memcached.conf \ + health.d/mysql.conf health.d/named.conf health.d/mongodb.conf \ + health.d/nginx.conf health.d/postgres.conf health.d/redis.conf \ + health.d/retroshare.conf health.d/squid.conf \ + health.d/varnish.conf health.d/web_log.conf health.d/zfs.conf \ + health.d/cpu.conf health.d/disks.conf health.d/entropy.conf \ + health.d/ipc.conf health.d/memory.conf health.d/net.conf \ + health.d/netfilter.conf health.d/qos.conf health.d/ram.conf \ + health.d/softnet.conf health.d/swap.conf \ health.d/tcp_resets.conf health.d/udp_errors.conf DATA = $(dist_chartsconfig_DATA) $(dist_config_DATA) \ $(dist_healthconfig_DATA) $(dist_nodeconfig_DATA) \ - $(dist_pythonconfig_DATA) + $(dist_pythonconfig_DATA) $(dist_statsdconfig_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -282,6 +347,7 @@ dist_config_DATA = \ nodeconfigdir = $(configdir)/node.d dist_nodeconfig_DATA = \ node.d/README.md \ + node.d/fronius.conf.md \ node.d/named.conf.md \ node.d/sma_webbox.conf.md \ node.d/snmp.conf.md \ @@ -293,12 +359,14 @@ dist_pythonconfig_DATA = \ python.d/apache_cache.conf \ python.d/bind_rndc.conf \ python.d/cpufreq.conf \ + python.d/dns_query_time.conf \ python.d/dovecot.conf \ python.d/elasticsearch.conf \ python.d/example.conf \ python.d/exim.conf \ python.d/fail2ban.conf \ python.d/freeradius.conf \ + python.d/go_expvar.conf \ python.d/haproxy.conf \ python.d/hddtemp.conf \ python.d/ipfs.conf \ @@ -313,8 +381,10 @@ dist_pythonconfig_DATA = \ python.d/phpfpm.conf \ python.d/postfix.conf \ python.d/postgres.conf \ + python.d/rabbitmq.conf \ python.d/redis.conf \ python.d/retroshare.conf \ + python.d/samba.conf \ python.d/sensors.conf \ python.d/squid.conf \ python.d/smartd_log.conf \ @@ -328,12 +398,13 @@ dist_healthconfig_DATA = health.d/apache.conf health.d/backend.conf \ health.d/bind_rndc.conf health.d/elasticsearch.conf \ health.d/fping.conf health.d/haproxy.conf health.d/ipfs.conf \ health.d/ipmi.conf health.d/isc_dhcpd.conf \ - health.d/mdstat.conf health.d/memcached.conf \ - health.d/mysql.conf health.d/named.conf health.d/nginx.conf \ + health.d/lighttpd.conf health.d/mdstat.conf \ + health.d/memcached.conf health.d/mysql.conf \ + health.d/named.conf health.d/mongodb.conf health.d/nginx.conf \ health.d/postgres.conf health.d/redis.conf \ health.d/retroshare.conf health.d/squid.conf \ - health.d/varnish.conf health.d/web_log.conf $(NULL) \ - $(am__append_1) + health.d/varnish.conf health.d/web_log.conf health.d/zfs.conf \ + $(NULL) $(am__append_1) chartsconfigdir = $(configdir)/charts.d dist_chartsconfig_DATA = \ charts.d/apache.conf \ @@ -357,6 +428,11 @@ dist_chartsconfig_DATA = \ charts.d/squid.conf \ $(NULL) +statsdconfigdir = $(configdir)/statsd.d +dist_statsdconfig_DATA = \ + statsd.d/example.conf \ + $(NULL) + all: all-am .SUFFIXES: @@ -392,8 +468,11 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_chartsconfigDATA: $(dist_chartsconfig_DATA) @$(NORMAL_INSTALL) - test -z "$(chartsconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(chartsconfigdir)" @list='$(dist_chartsconfig_DATA)'; test -n "$(chartsconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(chartsconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(chartsconfigdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -410,8 +489,11 @@ uninstall-dist_chartsconfigDATA: dir='$(DESTDIR)$(chartsconfigdir)'; $(am__uninstall_files_from_dir) install-dist_configDATA: $(dist_config_DATA) @$(NORMAL_INSTALL) - test -z "$(configdir)" || $(MKDIR_P) "$(DESTDIR)$(configdir)" @list='$(dist_config_DATA)'; test -n "$(configdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(configdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(configdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -428,8 +510,11 @@ uninstall-dist_configDATA: dir='$(DESTDIR)$(configdir)'; $(am__uninstall_files_from_dir) install-dist_healthconfigDATA: $(dist_healthconfig_DATA) @$(NORMAL_INSTALL) - test -z "$(healthconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(healthconfigdir)" @list='$(dist_healthconfig_DATA)'; test -n "$(healthconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(healthconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(healthconfigdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -446,8 +531,11 @@ uninstall-dist_healthconfigDATA: dir='$(DESTDIR)$(healthconfigdir)'; $(am__uninstall_files_from_dir) install-dist_nodeconfigDATA: $(dist_nodeconfig_DATA) @$(NORMAL_INSTALL) - test -z "$(nodeconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(nodeconfigdir)" @list='$(dist_nodeconfig_DATA)'; test -n "$(nodeconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(nodeconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(nodeconfigdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -464,8 +552,11 @@ uninstall-dist_nodeconfigDATA: dir='$(DESTDIR)$(nodeconfigdir)'; $(am__uninstall_files_from_dir) install-dist_pythonconfigDATA: $(dist_pythonconfig_DATA) @$(NORMAL_INSTALL) - test -z "$(pythonconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(pythonconfigdir)" @list='$(dist_pythonconfig_DATA)'; test -n "$(pythonconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pythonconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pythonconfigdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -480,11 +571,32 @@ uninstall-dist_pythonconfigDATA: @list='$(dist_pythonconfig_DATA)'; test -n "$(pythonconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pythonconfigdir)'; $(am__uninstall_files_from_dir) -tags: TAGS -TAGS: +install-dist_statsdconfigDATA: $(dist_statsdconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_statsdconfig_DATA)'; test -n "$(statsdconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(statsdconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(statsdconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(statsdconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(statsdconfigdir)" || exit $$?; \ + done -ctags: CTAGS -CTAGS: +uninstall-dist_statsdconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_statsdconfig_DATA)'; test -n "$(statsdconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(statsdconfigdir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: distdir: $(DISTFILES) @@ -521,7 +633,7 @@ check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: - for dir in "$(DESTDIR)$(chartsconfigdir)" "$(DESTDIR)$(configdir)" "$(DESTDIR)$(healthconfigdir)" "$(DESTDIR)$(nodeconfigdir)" "$(DESTDIR)$(pythonconfigdir)"; do \ + for dir in "$(DESTDIR)$(chartsconfigdir)" "$(DESTDIR)$(configdir)" "$(DESTDIR)$(healthconfigdir)" "$(DESTDIR)$(nodeconfigdir)" "$(DESTDIR)$(pythonconfigdir)" "$(DESTDIR)$(statsdconfigdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -577,7 +689,7 @@ info-am: install-data-am: install-dist_chartsconfigDATA install-dist_configDATA \ install-dist_healthconfigDATA install-dist_nodeconfigDATA \ - install-dist_pythonconfigDATA + install-dist_pythonconfigDATA install-dist_statsdconfigDATA install-dvi: install-dvi-am @@ -623,25 +735,28 @@ ps-am: uninstall-am: uninstall-dist_chartsconfigDATA \ uninstall-dist_configDATA uninstall-dist_healthconfigDATA \ - uninstall-dist_nodeconfigDATA uninstall-dist_pythonconfigDATA + uninstall-dist_nodeconfigDATA uninstall-dist_pythonconfigDATA \ + uninstall-dist_statsdconfigDATA .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-dist_chartsconfigDATA install-dist_configDATA \ - install-dist_healthconfigDATA install-dist_nodeconfigDATA \ - install-dist_pythonconfigDATA install-dvi install-dvi-am \ +.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ + ctags-am distclean distclean-generic distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dist_chartsconfigDATA \ + install-dist_configDATA install-dist_healthconfigDATA \ + install-dist_nodeconfigDATA install-dist_pythonconfigDATA \ + install-dist_statsdconfigDATA 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-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ - pdf-am ps ps-am uninstall uninstall-am \ + pdf-am ps ps-am tags-am uninstall uninstall-am \ uninstall-dist_chartsconfigDATA uninstall-dist_configDATA \ uninstall-dist_healthconfigDATA uninstall-dist_nodeconfigDATA \ - uninstall-dist_pythonconfigDATA + uninstall-dist_pythonconfigDATA \ + uninstall-dist_statsdconfigDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/conf.d/apps_groups.conf b/conf.d/apps_groups.conf index 4c5171b3d..43bf1352c 100644 --- a/conf.d/apps_groups.conf +++ b/conf.d/apps_groups.conf @@ -95,12 +95,14 @@ php: php* ftpd: proftpd in.tftpd vsftpd uwsgi: uwsgi unicorn: *unicorn* +puma: *puma* # ----------------------------------------------------------------------------- # database servers sql: mysqld* mariad* postgres* oracle_* ora_* nosql: mongod redis* memcached +timedb: prometheus *carbon-cache.py* *carbon-aggregator.py* *graphite/manage.py* *net.opentsdb.tools.TSDMain* # ----------------------------------------------------------------------------- # email servers @@ -111,7 +113,7 @@ email: dovecot imapd pop3d amavis* master zmstat* zmmailboxdmgr qmgr oqmgr # network, routing, VPN ppp: ppp* -vpn: openvpn pptp* cjdroute +vpn: openvpn pptp* cjdroute gvpe tincd wifi: hostapd wpa_supplicant routing: ospfd* ospf6d* bgpd isisd ripd ripngd pimd ldpd zebra vtysh bird* @@ -218,12 +220,41 @@ ups: upsmon upsd */nut/* media: mplayer vlc xine mediatomb omxplayer* kodi* xbmc* mediacenter eventlircd media: mpd minidlnad mt-daapd avahi* Plex* +# ----------------------------------------------------------------------------- +# java applications + +hdfsdatanode: *org.apache.hadoop.hdfs.server.datanode.DataNode* +hdfsnamenode: *org.apache.hadoop.hdfs.server.namenode.NameNode* +hdfsjournalnode: *org.apache.hadoop.hdfs.qjournal.server.JournalNode* +hdfszkfc: *org.apache.hadoop.hdfs.tools.DFSZKFailoverController* + +yarnnode: *org.apache.hadoop.yarn.server.nodemanager.NodeManager* +yarnmgr: *org.apache.hadoop.yarn.server.resourcemanager.ResourceManager* +yarnproxy: *org.apache.hadoop.yarn.server.webproxy.WebAppProxyServer* + +sparkworker: *org.apache.spark.deploy.worker.Worker* +sparkmaster: *org.apache.spark.deploy.master.Master* + +hbaseregion: *org.apache.hadoop.hbase.regionserver.HRegionServer* +hbaserest: *org.apache.hadoop.hbase.rest.RESTServer* +hbasethrift: *org.apache.hadoop.hbase.thrift.ThriftServer* +hbasemaster: *org.apache.hadoop.hbase.master.HMaster* + +zookeeper: *org.apache.zookeeper.server.quorum.QuorumPeerMain* + +hive2: *org.apache.hive.service.server.HiveServer2* +hivemetastore: *org.apache.hadoop.hive.metastore.HiveMetaStore* + +solr: *solr.install.dir* + +airflow: *airflow* + # ----------------------------------------------------------------------------- # X X: X Xorg xinit lightdm xdm pulseaudio gkrellm xfwm4 xfdesktop xfce* Thunar X: xfsettingsd xfconfd gnome-* gdm gconf* dconf* xfconf* *gvfs gvfs* kdm slim -X: evolution-* firefox chromium opera epiphany WebKit* +X: evolution-* firefox chromium opera vivaldi-bin epiphany WebKit* # ----------------------------------------------------------------------------- # Kernel / System @@ -240,7 +271,8 @@ kernel: fsnotify_mark kthrotld deferwq scsi_* # ----------------------------------------------------------------------------- # other application servers -crsproxy: crsproxy +kafka: *kafka.Kafka* + sidekiq: *sidekiq* java: java ipfs: ipfs diff --git a/conf.d/fping.conf b/conf.d/fping.conf index 82ee2332a..63a7f7acd 100644 --- a/conf.d/fping.conf +++ b/conf.d/fping.conf @@ -29,7 +29,7 @@ hosts="" # The time in milliseconds (1 sec = 1000 ms) to ping the hosts # by default 5 pings per host per iteration -# fping will now allow this to be below 20ms +# fping will not allow this to be below 20ms #ping_every="200" diff --git a/conf.d/health.d/fping.conf b/conf.d/health.d/fping.conf index 69251b182..43658fef6 100644 --- a/conf.d/health.d/fping.conf +++ b/conf.d/health.d/fping.conf @@ -28,7 +28,7 @@ families: * lookup: average -10s unaligned of average units: ms every: 10s - green: 300 + green: 500 red: 1000 warn: $this > $green OR $max > $red crit: $this > $red diff --git a/conf.d/health.d/lighttpd.conf b/conf.d/health.d/lighttpd.conf new file mode 100644 index 000000000..915907a4a --- /dev/null +++ b/conf.d/health.d/lighttpd.conf @@ -0,0 +1,14 @@ + +# make sure lighttpd is running + +template: lighttpd_last_collected_secs + on: lighttpd.requests + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + delay: down 5m multiplier 1.5 max 1h + info: number of seconds since the last successful data collection + to: webmaster + diff --git a/conf.d/health.d/mongodb.conf b/conf.d/health.d/mongodb.conf new file mode 100644 index 000000000..a80cb3112 --- /dev/null +++ b/conf.d/health.d/mongodb.conf @@ -0,0 +1,13 @@ + +# make sure mongodb is running + +template: mongodb_last_collected_secs + on: mongodb.read_operations + calc: $now - $last_collected_t + units: seconds ago + every: 10s + warn: $this > (($status >= $WARNING) ? ($update_every) : ( 5 * $update_every)) + crit: $this > (($status == $CRITICAL) ? ($update_every) : (60 * $update_every)) + delay: down 5m multiplier 1.5 max 1h + info: number of seconds since the last successful data collection + to: dba diff --git a/conf.d/health.d/net.conf b/conf.d/health.d/net.conf index 0232395ac..bd288817b 100644 --- a/conf.d/health.d/net.conf +++ b/conf.d/health.d/net.conf @@ -99,9 +99,9 @@ families: * calc: $this * 100 / (($1m_received_packets_rate < 1000)?(1000):($1m_received_packets_rate)) every: 10s units: % - warn: $this > (($status >= $WARNING)?(200):(1000)) - crit: $this > (($status >= $WARNING)?(1000):(2000)) + warn: $this > (($status >= $WARNING)?(200):(5000)) + crit: $this > (($status >= $WARNING)?(5000):(6000)) options: no-clear-notification - info: the % of the rate of received packets in the last 10 seconds, compared to the rate of the last minute + info: the % of the rate of received packets in the last 10 seconds, compared to the rate of the last minute (clear notification for this alarm will not be sent) to: sysadmin diff --git a/conf.d/health.d/ram.conf b/conf.d/health.d/ram.conf index d60df75b2..b99e5e226 100644 --- a/conf.d/health.d/ram.conf +++ b/conf.d/health.d/ram.conf @@ -1,7 +1,14 @@ + alarm: used_ram_to_ignore + on: system.ram + calc: ($zfs.arc_size.arcsz = nan)?(0):($zfs.arc_size.arcsz) + every: 10s + info: the amount of memory that is reported as used, but it is actually capable for resizing itself based on the system needs (eg. ZFS ARC) + alarm: ram_in_use on: system.ram - calc: $used * 100 / ($used + $cached + $free) +# calc: $used * 100 / ($used + $cached + $free) + calc: ($used - $used_ram_to_ignore) * 100 / ($used - $used_ram_to_ignore + $cached + $free) units: % every: 10s warn: $this > (($status >= $WARNING) ? (80) : (90)) diff --git a/conf.d/health.d/tcp_resets.conf b/conf.d/health.d/tcp_resets.conf index 49fb1b924..803c88a81 100644 --- a/conf.d/health.d/tcp_resets.conf +++ b/conf.d/health.d/tcp_resets.conf @@ -26,10 +26,10 @@ lookup: average -10s unaligned absolute of OutRsts units: tcp resets/s every: 10s - warn: $this > ((($1m_ipv4_tcp_resets_sent < 5)?(5):($1m_ipv4_tcp_resets_sent)) * (($status >= $WARNING) ? (1) : (4))) + warn: $this > ((($1m_ipv4_tcp_resets_sent < 5)?(5):($1m_ipv4_tcp_resets_sent)) * (($status >= $WARNING) ? (1) : (20))) delay: up 0 down 60m multiplier 1.2 max 2h options: no-clear-notification - info: average TCP RESETS this host is sending, over the last 10 seconds (this can be an indication that a port scan is made, or that a service running on this host has crashed) + info: average TCP RESETS this host is sending, over the last 10 seconds (this can be an indication that a port scan is made, or that a service running on this host has crashed; clear notification for this alarm will not be sent) to: sysadmin # ----------------------------------------------------------------------------- @@ -47,8 +47,8 @@ options: no-clear-notification lookup: average -10s unaligned absolute of AttemptFails units: tcp resets/s every: 10s - warn: $this > ((($1m_ipv4_tcp_resets_received < 5)?(5):($1m_ipv4_tcp_resets_received)) * (($status >= $WARNING) ? (1) : (4))) + warn: $this > ((($1m_ipv4_tcp_resets_received < 5)?(5):($1m_ipv4_tcp_resets_received)) * (($status >= $WARNING) ? (1) : (10))) delay: up 0 down 60m multiplier 1.2 max 2h options: no-clear-notification - info: average TCP RESETS this host is receiving, over the last 10 seconds (this can be an indication that a service this host needs, has crashed) + info: average TCP RESETS this host is receiving, over the last 10 seconds (this can be an indication that a service this host needs, has crashed; clear notification for this alarm will not be sent) to: sysadmin diff --git a/conf.d/health.d/web_log.conf b/conf.d/health.d/web_log.conf index c668959f5..d18088172 100644 --- a/conf.d/health.d/web_log.conf +++ b/conf.d/health.d/web_log.conf @@ -156,6 +156,7 @@ families: * delay: down 15m multiplier 1.5 max 1h options: no-clear-notification info: the percentage of successful web requests over the last 5 minutes, \ - compared with the previous 5 minutes + compared with the previous 5 minutes \ + (clear notification for this alarm will not be sent) to: webmaster diff --git a/conf.d/health.d/zfs.conf b/conf.d/health.d/zfs.conf new file mode 100644 index 000000000..af73824e6 --- /dev/null +++ b/conf.d/health.d/zfs.conf @@ -0,0 +1,10 @@ + + alarm: zfs_memory_throttle + on: zfs.memory_ops + lookup: sum -10m unaligned absolute of throttled + units: events + every: 1m + warn: $this > 0 + delay: down 1h multiplier 1.5 max 2h + info: the number of times ZFS had to limit the ARC growth in the last 10 minutes + to: sysadmin diff --git a/conf.d/health_alarm_notify.conf b/conf.d/health_alarm_notify.conf index 23776b96a..4d8444ed5 100644 --- a/conf.d/health_alarm_notify.conf +++ b/conf.d/health_alarm_notify.conf @@ -303,6 +303,68 @@ SEND_PD="YES" DEFAULT_RECIPIENT_PD="" +#------------------------------------------------------------------------------ +# custom notifications +# + +# enable/disable sending custom notifications +SEND_CUSTOM="YES" + +# if a role's recipients are not configured, use the following. +# (empty = do not send a notification for unconfigured roles) +DEFAULT_RECIPIENT_CUSTOM="" + +# The custom_sender() is a custom function to do whatever you need to do +custom_sender() { + # variables you can use: + # ${host} the host generated this event + # ${url_host} same as ${host} but URL encoded + # ${unique_id} the unique id of this event + # ${alarm_id} the unique id of the alarm that generated this event + # ${event_id} the incremental id of the event, for this alarm id + # ${when} the timestamp this event occurred + # ${name} the name of the alarm, as given in netdata health.d entries + # ${url_name} same as ${name} but URL encoded + # ${chart} the name of the chart (type.id) + # ${url_chart} same as ${chart} but URL encoded + # ${family} the family of the chart + # ${url_family} same as ${family} but URL encoded + # ${status} the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL + # ${old_status} the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL + # ${value} the current value of the alarm + # ${old_value} the previous value of the alarm + # ${src} the line number and file the alarm has been configured + # ${duration} the duration in seconds of the previous alarm state + # ${duration_txt} same as ${duration} for humans + # ${non_clear_duration} the total duration in seconds this is/was non-clear + # ${non_clear_duration_txt} same as ${non_clear_duration} for humans + # ${units} the units of the value + # ${info} a short description of the alarm + # ${value_string} friendly value (with units) + # ${old_value_string} friendly old value (with units) + # ${image} the URL of an image to represent the status of the alarm + # ${color} a color in #AABBCC format for the alarm + # ${goto_url} the URL the user can click to see the netdata dashboard + + # these are more human friendly: + # ${alarm} like "name = value units" + # ${status_message} like "needs attention", "recovered", "is critical" + # ${severity} like "Escalated to CRITICAL", "Recovered from WARNING" + # ${raised_for} like "(alarm was raised for 10 minutes)" + + # example human readable SMS + local msg="${host} ${status_message}: ${alarm} ${raised_for}" + + # limit it to 160 characters and encode it for use in a URL + urlencode "${msg:0:160}" >/dev/null; msg="${REPLY}" + + # a space separated list of the recipients to send alarms to + to="${1}" + + info "not sending custom notification to ${to}, for ${status} of '${host}.${chart}.${name}' - custom_sender() is not configured." +} + + ############################################################################### # RECIPIENTS PER ROLE @@ -330,6 +392,8 @@ role_recipients_messagebird[sysadmin]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" role_recipients_pd[sysadmin]="${DEFAULT_RECIPIENT_PD}" +role_recipients_custom[sysadmin]="${DEFAULT_RECIPIENT_CUSTOM}" + # ----------------------------------------------------------------------------- # DNS related alarms @@ -353,6 +417,8 @@ role_recipients_messagebird[domainadmin]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" role_recipients_pd[domainadmin]="${DEFAULT_RECIPIENT_PD}" +role_recipients_custom[domainadmin]="${DEFAULT_RECIPIENT_CUSTOM}" + # ----------------------------------------------------------------------------- # database servers alarms # mysql, redis, memcached, postgres, etc @@ -377,6 +443,8 @@ role_recipients_messagebird[dba]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" role_recipients_pd[dba]="${DEFAULT_RECIPIENT_PD}" +role_recipients_custom[dba]="${DEFAULT_RECIPIENT_CUSTOM}" + # ----------------------------------------------------------------------------- # web servers alarms # apache, nginx, lighttpd, etc @@ -401,6 +469,8 @@ role_recipients_messagebird[webmaster]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" role_recipients_pd[webmaster]="${DEFAULT_RECIPIENT_PD}" +role_recipients_custom[webmaster]="${DEFAULT_RECIPIENT_CUSTOM}" + # ----------------------------------------------------------------------------- # proxy servers alarms # squid, etc @@ -424,3 +494,5 @@ role_recipients_twilio[proxyadmin]="${DEFAULT_RECIPIENT_TWILIO}" role_recipients_messagebird[proxyadmin]="${DEFAULT_RECIPIENT_MESSAGEBIRD}" role_recipients_pd[proxyadmin]="${DEFAULT_RECIPIENT_PD}" + +role_recipients_custom[proxyadmin]="${DEFAULT_RECIPIENT_CUSTOM}" diff --git a/conf.d/node.d/fronius.conf.md b/conf.d/node.d/fronius.conf.md new file mode 100644 index 000000000..c80afa0b5 --- /dev/null +++ b/conf.d/node.d/fronius.conf.md @@ -0,0 +1,67 @@ +[Fronius Symo 8.2](https://www.fronius.com/en/photovoltaics/products/all-products/inverters/fronius-symo/fronius-symo-8-2-3-m) + +The plugin has been tested with a single inverter, namely Fronius Symo 8.2-3-M: + +- Datalogger version: 240.162630 +- Software version: 3.7.4-6 +- Hardware version: 2.4D + +Other products and versions may work, but without any guarantees. + +Example netdata configuration for node.d/fronius.conf. Copy this section to fronius.conf and change name/ip. +The module supports any number of servers. Sometimes there is a lag when collecting every 3 seconds, so 5 should be okay too. You can modify this per server. +```json +{ + "enable_autodetect": false, + "update_every": 5, + "servers": [ + { + "name": "Solar", + "hostname": "symo.ip.or.dns", + "update_every": 5, + "api_path": "/solar_api/v1/GetPowerFlowRealtimeData.fcgi" + } + ] +} +``` + +The output of /solar_api/v1/GetPowerFlowRealtimeData.fcgi looks like this: +```json +{ + "Head" : { + "RequestArguments" : {}, + "Status" : { + "Code" : 0, + "Reason" : "", + "UserMessage" : "" + }, + "Timestamp" : "2017-07-05T12:35:12+02:00" + }, + "Body" : { + "Data" : { + "Site" : { + "Mode" : "meter", + "P_Grid" : -6834.549847, + "P_Load" : -1271.450153, + "P_Akku" : null, + "P_PV" : 8106, + "rel_SelfConsumption" : 15.685297, + "rel_Autonomy" : 100, + "E_Day" : 35020, + "E_Year" : 5826076, + "E_Total" : 14788870, + "Meter_Location" : "grid" + }, + "Inverters" : { + "1" : { + "DT" : 123, + "P" : 8106, + "E_Day" : 35020, + "E_Year" : 5826076, + "E_Total" : 14788870 + } + } + } + } +} +``` diff --git a/conf.d/python.d.conf b/conf.d/python.d.conf index 9ed346cdc..0a37e40ae 100644 --- a/conf.d/python.d.conf +++ b/conf.d/python.d.conf @@ -26,11 +26,13 @@ log_interval: 3600 # If "default_run" = "no" the default for all modules is disabled (no). # Setting any of these to "yes" will enable it. -# apache_cache: yes +# apache_cache has been replaced by web_log +apache_cache: no # apache: yes # bind_rndc: yes # cpufreq: yes # cpuidle: yes +# dns_query_time: yes # dovecot: yes # elasticsearch: yes @@ -43,7 +45,7 @@ example: no # gunicorn_log has been replaced by web_log gunicorn_log: no - +go_expvar: no # haproxy: yes # hddtemp: yes # ipfs: yes @@ -52,6 +54,7 @@ gunicorn_log: no # memcached: yes # mysql: yes # nginx: yes +# nsd: yes # nginx_log has been replaced by web_log nginx_log: no @@ -60,9 +63,11 @@ nginx_log: no # phpfpm: yes # postfix: yes # postgres: yes +# rabbitmq: yes # redis: yes # retroshare: yes # sensors: yes +# samba: yes # smartd_log: yes # squid: yes # tomcat: yes diff --git a/conf.d/python.d/dns_query_time.conf b/conf.d/python.d/dns_query_time.conf new file mode 100644 index 000000000..f4d4dbf92 --- /dev/null +++ b/conf.d/python.d/dns_query_time.conf @@ -0,0 +1,72 @@ +# netdata python.d.plugin configuration for dns_query_time +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +# update_every: 1 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# retries sets the number of retries to be made in case of failures. +# If unset, the default for python.d.plugin is used. +# Attempts to restore the service are made once every update_every +# and only if the module has collected values in the past. +# retries: 5 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear at the +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# retries: 5 # the JOB's number of restoration attempts +# +# Additionally to the above, dns_query_time also supports the following: +# +# dns_servers: 'dns servers' # list of dns servers to query +# domains: 'domains' # list of domains +# aggregate: yes/no # Default: yes. Aggregate all servers in one chart or not +# response_timeout: 4 # Defalt: 4. Dns query response timeout (query = -100 if response time > response_time) +# +# +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) +# +# +#aggregate: yes +#dns_servers: '8.8.8.8 8.8.4.4' +#domains: 'python.org distrowatch.com linuxmint.com linux.com rsyslog.com liblognorm.com archlinux.org cisco.com debian.org kernel.org gns3.com opera.com github.com youtube.com amazon.co.uk kde.org netdata.firehol.org ubuntu.com redhat.com opensuse.org wireshark.org vmware.com microsoft.com elastic.co' diff --git a/conf.d/python.d/elasticsearch.conf b/conf.d/python.d/elasticsearch.conf index f98aaeced..7c35df229 100644 --- a/conf.d/python.d/elasticsearch.conf +++ b/conf.d/python.d/elasticsearch.conf @@ -61,19 +61,16 @@ # cluster_health: False/True # Calls to cluster health elasticsearch API. Enabled by default. # cluster_stats: False/True # Calls to cluster stats elasticsearch API. Enabled by default. # -# ---------------------------------------------------------------------- -# IMPORTANT Information -# -# Module uses python `requests` package # -# You need to install it manually. (python-requests or python3-requests depending on the version of python). +# if the URL is password protected, the following are supported: # +# user: 'username' +# pass: 'password' # +# ---------------------------------------------------------------------- # AUTO-DETECTION JOBS # only one of them will run (they have the same name) # -#local: -# host: '127.0.0.1' -# port: '9200' -# cluster_health: True -# cluster_stats: True +local: + host: '127.0.0.1' + port: '9200' diff --git a/conf.d/python.d/fail2ban.conf b/conf.d/python.d/fail2ban.conf index d9664e353..76277108b 100644 --- a/conf.d/python.d/fail2ban.conf +++ b/conf.d/python.d/fail2ban.conf @@ -58,15 +58,6 @@ # # log_path: 'path to fail2ban.log' # Default: '/var/log/fail2ban.log' # conf_path: 'path to jail.local/jail.conf' # Default: '/etc/fail2ban/jail.local' -# conf_dir: 'path to jail.d/' # Default: '' empty +# conf_dir: 'path to jail.d/' # Default: '/etc/fail2ban/jail.d/' # exclude: 'jails you want to exclude from autodetection' # Default: '[]' empty list #------------------------------------------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------------------------------------------ -# AUTO-DETECTION JOBS -# only one of them will run (they have the same name) - -local: - log_path: '/var/log/fail2ban.log' - conf_path: '/etc/fail2ban/jail.local' -# conf_dir: '/etc/fail2ban/jail.d/' -# exclude: 'dropbear apache' diff --git a/conf.d/python.d/go_expvar.conf b/conf.d/python.d/go_expvar.conf new file mode 100644 index 000000000..5be4890dc --- /dev/null +++ b/conf.d/python.d/go_expvar.conf @@ -0,0 +1,106 @@ +# netdata python.d.plugin configuration for go_expvar +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +# update_every: 1 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# retries sets the number of retries to be made in case of failures. +# If unset, the default for python.d.plugin is used. +# Attempts to restore the service are made once every update_every +# and only if the module has collected values in the past. +# retries: 5 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: my_name # the JOB's name as it will appear at the +# # dashboard. If name: is not supplied the +# # job_name: will be used (use _ for spaces) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# retries: 5 # the JOB's number of restoration attempts +# +# Additionally to the above, this plugin also supports the following: +# +# url: 'http://127.0.0.1/debug/vars' # the URL of the expvar endpoint +# ss_cert: # ignore HTTPS self-signed certificate +# proxy: # use HTTP proxy +# +# As the plugin cannot possibly know the port your application listens on, there is no default value. Please include +# the whole path of the endpoint, as the expvar handler can be installed in a non-standard location. +# +# if the URL is password protected, the following are supported: +# +# user: 'username' +# pass: 'password' +# +# collect_memstats: true # enables charts for Go runtime's memory statistics +# extra_charts: {} # defines extra data/charts to monitor, please see the example below +# +# If collect_memstats is disabled and no extra charts are defined, this module will disable itself, as it has no data to +# collect. +# +# Please visit the module wiki page for more information on how to use the extra_charts variable: +# +# https://github.com/firehol/netdata/wiki/Monitoring-Go-Applications#monitoring-custom-vars-with-go_expvar +# +# Configuration example +# --------------------- + +#app1: +# name : 'app1' +# url : 'http://127.0.0.1:8080/debug/vars' +# collect_memstats: true +# extra_charts: +# - id: "runtime_goroutines" +# options: +# name: num_goroutines +# title: "runtime: number of goroutines" +# units: goroutines +# family: runtime +# context: expvar.runtime.goroutines +# chart_type: line +# lines: +# - {expvar_key: 'runtime.goroutines', expvar_type: int, id: runtime_goroutines} +# - id: "foo_counters" +# options: +# name: counters +# title: "some random counters" +# units: awesomeness +# family: counters +# context: expvar.foo.counters +# chart_type: line +# lines: +# - {expvar_key: 'counters.cnt1', expvar_type: int, id: counters_cnt1} +# - {expvar_key: 'counters.cnt2', expvar_type: float, id: counters_cnt2} + diff --git a/conf.d/python.d/isc_dhcpd.conf b/conf.d/python.d/isc_dhcpd.conf index 7c8fe3ceb..938ca6e72 100644 --- a/conf.d/python.d/isc_dhcpd.conf +++ b/conf.d/python.d/isc_dhcpd.conf @@ -56,8 +56,11 @@ # # Additionally to the above, isc_dhcpd supports the following: # -# leases_path: 'PATH' # the path to dhcpd.leases file -# pools: 'dhcpd pools list' # Pools in CIDR format +# leases_path: 'PATH' # the path to dhcpd.leases file +# pools: +# office: '192.168.2.0/24' # name(dimension): pool in CIDR format +# wifi: '192.168.3.0/24' # name(dimension): pool in CIDR format +# 192.168.4.0/24: '192.168.4.0/24' # name(dimension): pool in CIDR format # #----------------------------------------------------------------------- # IMPORTANT notes @@ -75,4 +78,6 @@ # #leases: # leases_path : '/var/lib/dhcp/dhcpd.leases' -# pools : '192.168.3.0/24 192.168.4.0/24 192.168.5.0/24' +# pools: +# office: '192.168.2.0/24' +# wifi: '192.168.3.0/24' diff --git a/conf.d/python.d/postgres.conf b/conf.d/python.d/postgres.conf index d4d2bafcc..12dddae67 100644 --- a/conf.d/python.d/postgres.conf +++ b/conf.d/python.d/postgres.conf @@ -68,6 +68,7 @@ # # table_stats : false # index_stats : false +# database_poll : 'dbase_name1 dbase_name2' # poll only specified databases (all other will be excluded from charts) # # Postfix permissions are configured at its pg_hba.conf file. You can # "trust" local clients to allow netdata to connect, or you can create diff --git a/conf.d/python.d/rabbitmq.conf b/conf.d/python.d/rabbitmq.conf new file mode 100644 index 000000000..eccf65df9 --- /dev/null +++ b/conf.d/python.d/rabbitmq.conf @@ -0,0 +1,75 @@ +# netdata python.d.plugin configuration for rabbitmq +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +# update_every: 1 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# retries sets the number of retries to be made in case of failures. +# If unset, the default for python.d.plugin is used. +# Attempts to restore the service are made once every update_every +# and only if the module has collected values in the past. +# retries: 5 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear at the +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# retries: 5 # the JOB's number of restoration attempts +# +# Additionally to the above, rabbitmq plugin also supports the following: +# +# host: 'ipaddress' # Server ip address or hostname. Default: 127.0.0.1 +# port: 'port' # Rabbitmq port. Default: 15672 +# scheme: 'scheme' # http or https. Default: http +# +# if the URL is password protected, the following are supported: +# +# user: 'username' +# pass: 'password' +# +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) +# +local: + host: '127.0.0.1' + user: 'guest' + pass: 'guest' diff --git a/conf.d/python.d/samba.conf b/conf.d/python.d/samba.conf new file mode 100644 index 000000000..865281cd6 --- /dev/null +++ b/conf.d/python.d/samba.conf @@ -0,0 +1,58 @@ +# netdata python.d.plugin configuration for samba +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +update_every: 5 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# retries sets the number of retries to be made in case of failures. +# If unset, the default for python.d.plugin is used. +# Attempts to restore the service are made once every update_every +# and only if the module has collected values in the past. +# retries: 5 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear at the +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# retries: 5 # the JOB's number of restoration attempts +# +# + diff --git a/conf.d/python.d/smartd_log.conf b/conf.d/python.d/smartd_log.conf index e16454dfb..8764ffd3e 100644 --- a/conf.d/python.d/smartd_log.conf +++ b/conf.d/python.d/smartd_log.conf @@ -58,11 +58,11 @@ # # log_path: '/path/to/smartdlogs' # path to smartd log files. Default is /var/log/smartd # raw_values: no # raw or normalized values on charts. Default is normalized. -# smart_attributes: '1 2 3 4 44' # add additional smart attributes charts. Default are ['1', '4', '5', '7', '9', '12', '193', '194', '197', '198', '200']. +# smart_attributes: '1 2 3 4 44' # smart attributes charts. Default are ['1', '4', '5', '7', '9', '12', '193', '194', '197', '198', '200']. # # ---------------------------------------------------------------------- # Additional information -# Plugin reads smartd log files (-A option). +# Plugin reads smartd log files (-A option). # You need to add (man smartd) to /etc/default/smartmontools '-i 600 -A /var/log/smartd/' to pass additional options to smartd on startup # Then restart smartd service and check /path/log/smartdlogs # ls /var/log/smartd/ @@ -74,12 +74,12 @@ # RAW vs NORMALIZED values # "Normalized value", commonly referred to as just "value". This is a most universal measurement, on the scale from 0 (bad) to some maximum (good) value. # Maximum values are typically 100, 200 or 253. Rule of thumb is: high values are good, low values are bad. -# +# # "Raw value" - the value of the attribute as it is tracked by the device, before any normalization takes place. # Some raw numbers provide valuable insight when properly interpreted. These cases will be discussed later on. # Raw values are typically listed in hexadecimal numbers. The raw value has different structure for different vendors and is often not meaningful as a decimal number. # # # JOB configuration -# +# log_path: '/var/log/smartd' diff --git a/conf.d/python.d/web_log.conf b/conf.d/python.d/web_log.conf index 06656285f..e51b565d6 100644 --- a/conf.d/python.d/web_log.conf +++ b/conf.d/python.d/web_log.conf @@ -60,13 +60,21 @@ # Additionally to the above, web_log also supports the following: # # path: 'PATH' # the path to web server log file -# detailed_response_codes: yes/no # Default: yes. Additional chart where response codes are not grouped -# detailed_response_aggregate: yes/no # Default: yes. Not aggregated detailed response codes charts -# all_time : yes/no # Default: yes. All time unique client IPs chart (50000 addresses ~ 400KB) +# path: 'PATH[0-9]*[0-9]' # log files with date suffix are also supported +# detailed_response_codes: yes/no # default: yes. Additional chart where response codes are not grouped +# detailed_response_aggregate: yes/no # default: yes. Not aggregated detailed response codes charts +# all_time : yes/no # default: yes. All time unique client IPs chart (50000 addresses ~ 400KB) +# filter: # filter with regex +# include: 'REGEX' # only those rows that matches the regex +# exclude: 'REGEX' # all rows except those that matches the regex # categories: # requests per url chart configuration # cacti: 'cacti.*' # name(dimension): REGEX to match # observium: 'observium.*' # name(dimension): REGEX to match # stub_status: 'stub_status' # name(dimension): REGEX to match +# user_defined: # requests per pattern in field (custom_log_format) +# cacti: 'cacti.*' # name(dimension): REGEX to match +# observium: 'observium.*' # name(dimension): REGEX to match +# stub_status: 'stub_status' # name(dimension): REGEX to match # custom_log_format: # define a custom log format # pattern: '(?P
    [\da-f.:]+) -.*?"(?P[A-Z]+) (?P.*?)" (?P[1-9]\d{2}) (?P\d+) (?P\d+) (?P\d\.\d+) ' # time_multiplier: 1000000 # type - convert time to microseconds @@ -83,7 +91,7 @@ # nginx: # log_format netdata '$remote_addr - $remote_user [$time_local] ' # '"$request" $status $body_bytes_sent ' -# '$request_length $request_time ' +# '$request_length $request_time $upstream_response_time ' # '"$http_referer" "$http_user_agent"'; # access_log /var/log/nginx/access.log netdata; # @@ -145,3 +153,35 @@ gunicorn_log: gunicorn_log2: name: 'gunicorn' path: '/var/log/gunicorn/gunicorn-access.log' + +# ------------------------------------------- +# Apache Cache +apache_cache: + name: 'apache_cache' + type: 'apache_cache' + path: '/var/log/apache/cache.log' + +apache2_cache: + name: 'apache_cache' + type: 'apache_cache' + path: '/var/log/apache2/cache.log' + +httpd_cache: + name: 'apache_cache' + type: 'apache_cache' + path: '/var/log/httpd/cache.log' + +# ------------------------------------------- +# Squid + +# debian/ubuntu +squid_log1: + name: 'squid' + type: 'squid' + path: '/var/log/squid3/access.log' + +#gentoo +squid_log2: + name: 'squid' + type: 'squid' + path: '/var/log/squid/access.log' diff --git a/conf.d/statsd.d/example.conf b/conf.d/statsd.d/example.conf new file mode 100644 index 000000000..0af9dd27d --- /dev/null +++ b/conf.d/statsd.d/example.conf @@ -0,0 +1,65 @@ +# statsd synthetic charts configuration + +# You can add many .conf files, one for each of your apps + +# start a new app - you can add many apps in the same file +[app] + # give a name for this app + # this controls the main menu on the dashboard + # and will be the prefix for all charts of the app + name = myapp + + # match all the metrics of the app + metrics = myapp.* + + # shall private charts of these metrics be created? + private charts = no + + # shall gaps be shown when metrics are not collected? + gaps when not collected = no + + # the memory mode for the charts of this app: none|map|save + # the default is to use the global memory mode + #memory mode = ram + + # the history size for the charts of this app, in seconds + # the default is to use the global history + #history = 3600 + + + +# create a chart +# this is its id - the chart will be named myapp.mychart +[mychart] + # a name for the chart, similar to the id (2 names for each chart) + name = mychart + + # the chart title + title = my chart title + + # the submenu of the dashboard + family = my family + + # the context for alarm templates + context = chart.context + + # the units of the chart + units = tests/s + + # the sorting priority of the chart on the dashboard + priority = 91000 + + # the type of chart to create: line | area | stacked + type = area + + # one or more dimensions for the chart + # type = events | last | min | max | sum | average | percentile | median | stddev + # events = the number of events for this metric + # last = the last value collected + # all the others are only valid for histograms and timers + dimension = myapp.metric1 avg average 1 1 + dimension = myapp.metric1 lower min 1 1 + dimension = myapp.metric1 upper max 1 1 + dimension = myapp.metric2 other last 1 1 + +# You can add as many charts as needed diff --git a/conf.d/stream.conf b/conf.d/stream.conf index 0ebdccb8a..0ae5ba673 100644 --- a/conf.d/stream.conf +++ b/conf.d/stream.conf @@ -5,59 +5,64 @@ # number of hosts. # # You can generate API keys, with the linux command: uuidgen -# + + # ----------------------------------------------------------------------------- # 1. ON SLAVE NETDATA - THE ONE THAT WILL BE SENDING METRICS [stream] # Enable this on slaves, to have them send metrics. - enabled = no - - # The destination to send metrics to. - # A space separated list of: - # [PROTOCOL:]HOST[%INTERFACE][:PORT] - # The first available will get the metrics. - # PROTOCOL = tcp or udp (only tcp is supported by masters) - # HOST = an IPv4, IPv6 IP, or a hostname. - # IPv6 IPs should be given with brackets [ip:address] - # INTERFACE = the network interface to use - # PORT = the port number or service name (/etc/services) - # This communication is not HTTP (cannot be proxied by web proxies). - destination = - - # The API_KEY to use (as the sender) - api key = + enabled = no + + # Where is the receiving netdata? + # A space separated list of: + # + # [PROTOCOL:]HOST[%INTERFACE][:PORT] + # + # If many are given, the first available will get the metrics. + # + # PROTOCOL = tcp or udp (only tcp is supported by masters) + # HOST = an IPv4, IPv6 IP, or a hostname. + # IPv6 IPs should be given with brackets [ip:address] + # INTERFACE = the network interface to use + # PORT = the port number or service name (/etc/services) + # + # This communication is not HTTP (cannot be proxied by web proxies). + destination = + + # The API_KEY to use (as the sender) + api key = # The timeout to connect and send metrics - timeout seconds = 60 + timeout seconds = 60 - # If the destination line above does specify a port, use this - default port = 19999 + # If the destination line above does not specify a port, use this + default port = 19999 - # The buffer to use for sending metrics. - # 1MB by default is good for 2-3 seconds of data, so increase this - # if you expect latencies. - buffer size bytes = 1048576 + # The buffer to use for sending metrics. + # 1MB is good for 10-20 seconds of data, so increase this + # if you expect latencies. + buffer size bytes = 1048576 - # If the connection fails, or it disconnects, - # retry after that many seconds. - reconnect delay seconds = 5 + # If the connection fails, or it disconnects, + # retry after that many seconds. + reconnect delay seconds = 5 - # Attempt to sync the clock the of the master with the clock of the - # slave for that many iterations, when starting. - initial clock resync iterations = 60 + # Attempt to sync the clock the of the master with the clock of the + # slave for that many iterations, when starting. + initial clock resync iterations = 60 # ----------------------------------------------------------------------------- # 2. ON MASTER NETDATA - THE ONE THAT WILL BE RECEIVING METRICS -# + # You can have one API key per slave, or the same API key for all slaves. # -# All options below are used in this order: +# netdata searches for options in this order: # -# a) MACHINE_GUID (settings for each machine) -# b) API_KEY (settings for the API key) -# c) this netdata defaults (as in netdata.conf) +# a) [MACHINE_GUID] section (settings for each machine) +# b) [API_KEY] section (settings for the API key) +# c) master netdata settings (netdata.conf) # # You can combine the above (the more specific setting will be used). @@ -68,13 +73,13 @@ # Default settings for the API key # You can disable the API key, by setting this to: no - # The default (for unknown API keys) is also: no + # The default (for unknown API keys) is: no enabled = no # The default history in entries, for all hosts using this API key. # You can also set it per host below. # If you don't set it here, the history size of the central netdata - # will be used + # will be used. default history = 3600 # The default memory mode to be used for all hosts using this API key. @@ -84,11 +89,11 @@ # save save on exit, load on start # map like swap (continuously syncing to disks) # ram keep it in RAM, don't touch the disk - # none no database (passing through this netdata) + # none no database at all (use this on headless proxies) default memory mode = ram # Shall we enable health monitoring for the hosts using this API key? - # 3 values: + # 3 possible values: # yes enable alarms # no do not enable alarms # auto enable alarms, only when the sending netdata is connected @@ -107,16 +112,18 @@ # ----------------------------------------------------------------------------- -# 3. ON MASTER NETDATA - THE ONE THAT WILL BE RECEIVING METRICS -# -# THIS IS OPTIONAL - YOU DON'T NEED IT BY DEFAULT -# It only exists to give you finer control of the master settings for each +# 3. PER SENDING HOST SETTINGS, ON MASTER NETDATA +# THIS IS OPTIONAL - YOU DON'T NEED IT + +# This section exists to give you finer control of the master settings for each # slave host, when the same API key is used by many netdata slaves / proxies. # # Each netdata has a unique GUID - generated the first time netdata starts. # You can find it at /var/lib/netdata/registry/netdata.public.unique.id +# (at the slave). +# # The host sending data will have one. If the host is not ephemeral, -# you can give settings for each specific host here. +# you can give settings for each sending host here. [MACHINE_GUID] # enable this host: yes | no diff --git a/config.guess b/config.guess index d622a44e5..b79252d6b 100755 --- a/config.guess +++ b/config.guess @@ -1,14 +1,12 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012 Free Software Foundation, Inc. +# Copyright 1992-2013 Free Software Foundation, Inc. -timestamp='2012-02-10' +timestamp='2013-06-10' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -22,19 +20,17 @@ timestamp='2012-02-10' # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Originally written by Per Bothner. Please send patches (context -# diff format) to and include a ChangeLog -# entry. +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). # -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. +# Originally written by Per Bothner. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# +# Please send patches with a ChangeLog entry to config-patches@gnu.org. + me=`echo "$0" | sed -e 's,.*/,,'` @@ -54,9 +50,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 -Free Software Foundation, Inc. +Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -138,6 +132,27 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + ;; +esac + # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in @@ -200,6 +215,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} @@ -302,7 +321,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; - arm:riscos:*:*|arm:RISCOS:*:*) + arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) @@ -801,6 +820,9 @@ EOF i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; @@ -852,21 +874,21 @@ EOF exit ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in @@ -879,59 +901,54 @@ EOF EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-gnueabi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else - echo ${UNAME_MACHINE}-unknown-linux-gnueabihf + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-gnu + echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-gnu + echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) - LIBC=gnu - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build @@ -950,54 +967,63 @@ EOF #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; + or1k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; or32:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) - echo sparc-unknown-linux-gnu + echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu + echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu + echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-gnu + echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. @@ -1201,6 +1227,9 @@ EOF BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; @@ -1227,19 +1256,21 @@ EOF exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - case $UNAME_PROCESSOR in - i386) - eval $set_cc_for_build - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - UNAME_PROCESSOR="x86_64" - fi - fi ;; - unknown) UNAME_PROCESSOR=powerpc ;; - esac + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) @@ -1256,7 +1287,7 @@ EOF NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; - NSE-?:NONSTOP_KERNEL:*:*) + NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) @@ -1330,9 +1361,6 @@ EOF exit ;; esac -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 - eval $set_cc_for_build cat >$dummy.c < header file. */ #undef HAVE_RESOLV_H diff --git a/config.sub b/config.sub index c894da455..9633db704 100755 --- a/config.sub +++ b/config.sub @@ -1,24 +1,18 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012 Free Software Foundation, Inc. +# Copyright 1992-2013 Free Software Foundation, Inc. -timestamp='2012-02-10' +timestamp='2013-08-10' -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . @@ -26,11 +20,12 @@ timestamp='2012-02-10' # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). -# Please send patches to . Submit a context -# diff and a properly formatted GNU ChangeLog entry. +# Please send patches with a ChangeLog entry to config-patches@gnu.org. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. @@ -73,9 +68,7 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 -Free Software Foundation, Inc. +Copyright 1992-2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -123,7 +116,7 @@ esac maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) @@ -156,7 +149,7 @@ case $os in -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray | -microblaze) + -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; @@ -225,6 +218,12 @@ case $os in -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; -lynx*) os=-lynxos ;; @@ -253,10 +252,12 @@ case $basic_machine in | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ - | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ - | be32 | be64 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | be32 | be64 \ | bfin \ - | c4x | clipper \ + | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ @@ -267,7 +268,7 @@ case $basic_machine in | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | mcore | mep | metag \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ @@ -285,16 +286,17 @@ case $basic_machine in | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ - | nios | nios2 \ + | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 \ - | or32 \ + | or1k | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ @@ -364,13 +366,13 @@ case $basic_machine in | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | clipper-* | craynv-* | cydra-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ @@ -383,7 +385,8 @@ case $basic_machine in | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ @@ -401,12 +404,13 @@ case $basic_machine in | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | orion-* \ @@ -782,11 +786,15 @@ case $basic_machine in basic_machine=ns32k-utek os=-sysv ;; - microblaze) + microblaze*) basic_machine=microblaze-xilinx ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; mingw32) - basic_machine=i386-pc + basic_machine=i686-pc os=-mingw32 ;; mingw32ce) @@ -822,7 +830,7 @@ case $basic_machine in basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) - basic_machine=i386-pc + basic_machine=i686-pc os=-msys ;; mvs) @@ -1013,7 +1021,11 @@ case $basic_machine in basic_machine=i586-unknown os=-pw32 ;; - rdos) + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) basic_machine=i386-pc os=-rdos ;; @@ -1340,21 +1352,21 @@ case $os in -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ - | -sym* | -kopensolaris* \ + | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -openbsd* | -solidbsd* \ + | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -linux-gnu* | -linux-android* \ - | -linux-newlib* | -linux-uclibc* \ + | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ @@ -1486,9 +1498,6 @@ case $os in -aros*) os=-aros ;; - -kaos*) - os=-kaos - ;; -zvmoe) os=-zvmoe ;; @@ -1537,6 +1546,12 @@ case $basic_machine in c4x-* | tic4x-*) os=-coff ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; tic54x-*) os=-coff ;; @@ -1577,6 +1592,9 @@ case $basic_machine in mips*-*) os=-elf ;; + or1k-*) + os=-elf + ;; or32-*) os=-coff ;; diff --git a/configs.signatures b/configs.signatures index 713a1d323..d1308a882 100644 --- a/configs.signatures +++ b/configs.signatures @@ -2,6 +2,7 @@ declare -A configs_signatures=( ['0056936ce99788ed9ae1c611c87aa6d8']='apps_groups.conf' ['0102351817595a85d01ebd54a5f2f36b']='python.d/ovpn_status_log.conf' ['01302e01162d465614276de43fad7546']='python.d.conf' + ['01c54057e0ca55b5bb49df1662d6b8c3']='python.d/web_log.conf' ['02fa10fa85ab88e9723998de48d1aca0']='health.d/disks.conf' ['036dc300bd7b0e0ef229b9822686d63e']='python.d/isc_dhcpd.conf' ['0388b873d0d7e47c19005b7241db77d8']='python.d/tomcat.conf' @@ -20,8 +21,10 @@ declare -A configs_signatures=( ['0856124b1eecf01681b4fdf4e21efb3f']='health.d/net.conf' ['08ff5218f938fc48e09e718821169d14']='health.d/redis.conf' ['091572888425bc3b8b559c3c53367ec7']='apps_groups.conf' + ['09225283977a6584f8063016091cc4f2']='health.d/tcp_resets.conf' ['09264cec953ae1c4c2985e6446abb386']='health.d/mysql.conf' ['093540fdc2a228e976ce5d48a3adf9fc']='health.d/disks.conf' + ['09e030d26be08a13fa3560e47fa27825']='apps_groups.conf' ['0ad10fa896346202aee99384b0ec968d']='health.d/cpu.conf' ['0c5e0fa364d7bdf7c16e8459a0544572']='health.d/netfilter.conf' ['0cd4e1fb57497e4d4c2451a9e58f724d']='python.d/redis.conf' @@ -29,6 +32,8 @@ declare -A configs_signatures=( ['0dd38dcd2473ddb9f8b1b41147432d10']='health_alarm_notify.conf' ['0e59bc11d0a869ea0247c04c08c8d72e']='python.d/ipfs.conf' ['0ef8af1f358741afa7fd5d0ffabefaac']='charts.d/mysql.conf' + ['0f65b08edebedd06e376274021196a6b']='health.d/lighttpd.conf' + ['107e6ac69b30fb9837ac64c35f891ec7']='health.d/tcp_resets.conf' ['10c3b525850a1cb9de760a8ee96fbc6e']='charts.d/opensips.conf' ['1112c848ef91ebb9c622020d09712d67']='health.d/net.conf' ['13141998a5d71308d9c119834c27bfd3']='python.d.conf' @@ -44,7 +49,10 @@ declare -A configs_signatures=( ['1972e48345e6c3f0d65f94a03317622b']='health_alarm_notify.conf' ['1c12b678ab65f271a96da1bbd0a1ab1c']='health.d/softnet.conf' ['1c3168c95b53e999df3d45162b3f50b8']='health.d/fping.conf' + ['1d6efba856acaaaf3b50bc6d66611b92']='python.d/web_log.conf' + ['1e09f326178acf07d361c08a44d8b1f3']='python.d/rabbitmq.conf' ['1ea8e8ef1fa8a3a0fcdfba236f4cb195']='python.d/mysql.conf' + ['1eb0bc80934a3166fcde4d153c476d14']='health.d/fping.conf' ['1ef0fd38e7969c023bc3fa6d89eaf6d6']='python.d/mdstat.conf' ['1f5545b3ff52b3eb75ee05401f67a9bc']='fping.conf' ['1fa47f32ab52a22f8e7087cae85dd25e']='health.d/net.conf' @@ -56,6 +64,7 @@ declare -A configs_signatures=( ['22ceb822983134a7ca67343241f30341']='health.d/disks.conf' ['2385e5d35b440619621c4af62492d91b']='health.d/disks.conf' ['23ae815aefa221b1929f96752a1f7556']='health.d/squid.conf' + ['243503ceee1d5b4e1e55a28768a116ae']='health.d/net.conf' ['2472e49550326f7142e2c425ccbca005']='health.d/softnet.conf' ['254de8ec49602bea2da3631676d7cfec']='health.d/cpu.conf' ['256a7f06f7e579a61752fc64418cffe5']='charts.d/nut.conf' @@ -72,6 +81,7 @@ declare -A configs_signatures=( ['2f3a8e33df83f14e0af8ca2465697215']='python.d/exim.conf' ['2f4a85fedecce1bf425fa1039f6b021e']='apps_groups.conf' ['2fa8fb929fd597f2ab97b6efc540a043']='health_alarm_notify.conf' + ['307ac41f6c67fcf007d6f7135fac314c']='stream.conf' ['312b4b8e2805e19cf9be554b319567d6']='health.d/softnet.conf' ['318bb45755726a25120bb33413d4b582']='health.d/net.conf' ['325617412a628e3bc776e3fbb777a2a6']='health.d/redis.conf' @@ -121,9 +131,11 @@ declare -A configs_signatures=( ['47180421d580baeaedf8c0ef3d647fb5']='python.d/hddtemp.conf' ['48195c5c8c0476a49b714b4c76bdb570']='python.d/squid.conf' ['48eef63bcf744bae114b502b6dacb4a1']='charts.d/phpfpm.conf' + ['4960852f8951b54ca2fe10065752143e']='python.d.conf' ['4a448831776de8acf2e0bdc4cc994cb4']='apps_groups.conf' ['4b775fb31342f1478b3773d041a72911']='python.d.conf' ['4ccb06fff1ce06dc5bc80e0a9f568f6e']='charts.d.conf' + ['4cd585f5dfdacaf287413ad037b4e60a']='apps_groups.conf' ['4d13684cadfa90e73ab465409bf7263b']='health.d/mysql.conf' ['4d91ee6fe4c887ea3865ef36ac63da3c']='health.d/mysql.conf' ['4e995acb0d6fd77403a2a9dca984b55b']='charts.d.conf' @@ -132,6 +144,7 @@ declare -A configs_signatures=( ['4fdf72784296326e0b46cb526a5d77a1']='python.d.conf' ['4fef19afccd9a591165b72f0b1a2ac2e']='python.d/freeradius.conf' ['501eb2484b459b410b3f792c2dbaa955']='health.d/swap.conf' + ['5050b5963599f13ad5dc0263fa39a906']='python.d/fail2ban.conf' ['508771d8e4611a058991a1bc11039dea']='health.d/disks.conf' ['5120492fa26be3749192607f62dc05f8']='health.d/mdstat.conf' ['5271cf9fc0fd10915a9759add70f7d78']='health.d/swap.conf' @@ -140,6 +153,7 @@ declare -A configs_signatures=( ['53160707fdc6ce46c195b1b55bb0bcb1']='health.d/swap.conf' ['535e5113b07b0fc6f3abd59546c276f6']='charts.d.conf' ['5379cdc26d7725e2b0d688d785816cef']='python.d/mysql.conf' + ['5452eccad2f220d1191411737f6f4b2b']='python.d/isc_dhcpd.conf' ['54614490a14e1a4b7b3d9fecb6b4cfa5']='python.d/exim.conf' ['547779cdc460a926980de1590294b96b']='health.d/softnet.conf' ['55608bdd908a3806df1468f6ee318b2b']='health.d/qos.conf' @@ -151,6 +165,7 @@ declare -A configs_signatures=( ['5829812db29598db5857c9f433e96fef']='python.d/apache.conf' ['58e835b7176865ec5a6f59f7aba832bf']='health.d/named.conf' ['598f9814966a9e2fe48e8218151d3fa6']='stream.conf' + ['59dded33e3adfe622f36c557a4f4bed7']='health.d/net.conf' ['5b917d894bb6a755d59264e9d48e9d56']='fping.conf' ['5bbef0708f5eff4d4a53aaf35fc48a62']='health.d/disks.conf' ['5bf51bb24fb41db9b1e448bd060d3f8c']='apps_groups.conf' @@ -174,6 +189,7 @@ declare -A configs_signatures=( ['650b5fc9da23b25ee7ee1481e4aa2851']='health_alarm_notify.conf' ['653e0c014c8fcfb4db6cd3351d87d720']='python.d.conf' ['6546909d10cc5efcef9dd873bea85956']='python.d/mysql.conf' + ['65a59d96c039d0180603ffd945a8968c']='apps_groups.conf' ['65c6933a17fb6b7f8e6baeab73431c17']='charts.d/apcupsd.conf' ['6608c6546b3c6bde084fc1d34b1163c1']='health.d/retroshare.conf' ['669ebef43ee341f6889d382e86d0e200']='health.d/named.conf' @@ -181,6 +197,8 @@ declare -A configs_signatures=( ['6814b9bc84483db428f6a479ba221855']='python.d/mysql.conf' ['6a18f61a595c0d48c3363bcc0dbfa6b9']='health_alarm_notify.conf' ['6a47af861ad3dd112124c37fbf09672b']='apps_groups.conf' + ['6aa4507f86657383917a0407f2a9cc0d']='python.d.conf' + ['6acad8ce5c33e642742825db0eb9bb56']='python.d/web_log.conf' ['6b39de5d85db45115db236347a6896d4']='health.d/named.conf' ['6b917300747e7e8314844237e2462261']='python.d/apache_cache.conf' ['6bb278bd9e171c4cb5c0fe639231288b']='python.d/web_log.conf' @@ -220,7 +238,9 @@ declare -A configs_signatures=( ['7d8bd884ec26cb35d16c4fc05f969799']='python.d/squid.conf' ['7deb236ec68a512b9bdd18e6a51d76f7']='python.d/mysql.conf' ['7e5fc1644aa7a54f9dbb1bd102521b09']='health.d/memcached.conf' + ['7f13631183fbdf79c21c8e5a171e9b34']='health.d/zfs.conf' ['80266bddd3df374923c750a6de91d120']='health.d/apache.conf' + ['803a7f9dcb942eeac0fd764b9e3e38ca']='fping.conf' ['80d242d619eb7e91cebfdbf58d79b0f8']='health.d/disks.conf' ['80df37b89e852d585209b8c02bb94312']='python.d/bind_rndc.conf' ['80f109ff293ac94222bf3959432751bd']='health.d/qos.conf' @@ -228,6 +248,7 @@ declare -A configs_signatures=( ['8213d921b6a8382e27052fb42d81db3d']='python.d/freeradius.conf' ['8214bb8f4b005aa4691fcd38f7331e8f']='health.d/swap.conf' ['837480f77ba1a85677a36747fbc2cd2e']='python.d/sensors.conf' + ['8422e71761d22e817e3cfcb1befc6080']='health.d/mongodb.conf' ['8425a60ea3d28ed40bb0bac4c3f182e8']='python.d/sensors.conf' ['842b1ad5b89bfa5f421d9c5b72e001a4']='health.d/apache.conf' ['845023f9b4a526aa0e6493756dbe6034']='health.d/squid.conf' @@ -235,6 +256,7 @@ declare -A configs_signatures=( ['8490f690d97adacc4e2096df82e7e8a5']='charts.d/cpufreq.conf' ['871bbeea33b83ea9755600b6d574919c']='python.d/web_log.conf' ['87224d2f2b87646f3c0d38cc1eb30112']='python.d/nsd.conf' + ['87642c568093daf3b2c30c5beffe2225']='python.d/elasticsearch.conf' ['8810140ce9c09af1d18b9602c4003904']='health_alarm_notify.conf' ['88f77865f75c9fb61c97d700bd4561ee']='python.d/mysql.conf' ['8989b5e2f4ef9cd278ef58be0fae4074']='health.d/disks.conf' @@ -256,6 +278,7 @@ declare -A configs_signatures=( ['97eee7a30e6419df4537242e9d4a719d']='health.d/mysql.conf' ['97f337eb96213f3ede05e522e3743a6c']='python.d/memcached.conf' ['99a3de85d1e7826ed64a5f8576712e5d']='python.d.conf' + ['99b06e68f1da5917ae4cf60e901439f6']='health.d/ram.conf' ['99b6030ce25c8fee4598179c0f95fb0b']='health.d/redis.conf' ['99c1617448abbdc493976ab9bda5ce02']='apps_groups.conf' ['9a8a459a3841b78d4c6ef07428ad2fe1']='health.d/entropy.conf' @@ -270,6 +293,7 @@ declare -A configs_signatures=( ['a09714b5942cf25a89ec3da1dbc18063']='health.d/ram.conf' ['a0b3a12389c9c56dfe35964b20b59836']='health.d/bind_rndc.conf' ['a0ee8f351f213c0e8af9eb7a4a09cb95']='apps_groups.conf' + ['a1b6dfe312b896b0b1ba471e8ac07f95']='python.d/isc_dhcpd.conf' ['a2944a309f8ce1a3195451856478d6ae']='python.d.conf' ['a2a647dc492dc2d6ed1f5c0fdc97a96e']='python.d/mongodb.conf' ['a305b400378d6492efd15f9940c2779b']='health.d/softnet.conf' @@ -278,6 +302,7 @@ declare -A configs_signatures=( ['a4a8660728c6afcb528cc6b378897d6b']='health.d/squid.conf' ['a4be524cc5b7192878c292a17c767c28']='health.d/redis.conf' ['a5114d5b0d3816dba75024b9444f4b40']='health.d/disks.conf' + ['a5134d7cfbe27f5791e788c2add51abb']='apps_groups.conf' ['a55133f1b0be0a4255057849dd451b09']='health_alarm_notify.conf' ['a6d5ce2572bf7a1dce9e545fcd29273e']='health.d/apache.conf' ['a71d9082410200bf92e823675d78121c']='python.d/retroshare.conf' @@ -294,17 +319,21 @@ declare -A configs_signatures=( ['a9ab68845db2fb695b7060273a6ac68e']='health_alarm_notify.conf' ['a9cd91675467c5426f5b51c47602c889']='apps_groups.conf' ['aa4bee249bfc0c4a88ac8c2ffb97aa0d']='health.d/squid.conf' + ['aa620b7017c8b864d80aa6c8acab01cf']='python.d/smartd_log.conf' + ['aa6c4a270e6276f2deddf127ee1a24f6']='statsd.d/example.conf' ['aa8b57a733c2035917acf81a8ebdfbe7']='health.d/haproxy.conf' ['aac44691a1cf95fa8f8990a79bab4ce1']='python.d/web_log.conf' ['abaf2e021f9f6ee5d1c4e4726f47348e']='health.d/ipc.conf' ['acaa6731a272f6d251afb357e99b518f']='apps_groups.conf' ['ade389c1b6efe0cff47c33e662731f0a']='python.d/squid.conf' ['ae5ac0a3521e50aa6f6eda2a330b4075']='python.d/example.conf' + ['af14667ee7993acea810f6d50923bdc9']='health.d/web_log.conf' ['af44cc53aa2bc5cc8935667119567522']='python.d.conf' ['afdae4646c755ff2d117527fbf761c8e']='health.d/disks.conf' ['b07eebc6f58d19721ac069171b911d2a']='health_alarm_notify.conf' ['b0c59b2bd7a10f6a3f2be6b4b27857db']='health.d/haproxy.conf' ['b0f0a0ac415e4b1a82187b80d211e83b']='python.d/mysql.conf' + ['b181dcca01a258d9792ad703583baed2']='statsd.d/example.conf' ['b185914d4f795e1732273dc4c7a35845']='health.d/memory.conf' ['b27f10a38a95edbbec20f44a4728b7c4']='python.d.conf' ['b32164929eda7449a9677044e11151bf']='python.d.conf' @@ -312,6 +341,7 @@ declare -A configs_signatures=( ['b5b5a8d6d991fb1cef8d80afa23ba114']='python.d/cpufreq.conf' ['b636e5e603f9d93e52c7577ac8c6bf0c']='health.d/entropy.conf' ['b68706bb8101ef85192db92f865a5d80']='health_alarm_notify.conf' + ['b6ee82968de8fbf974c0d35b55fe6fae']='python.d/web_log.conf' ['b735732fbe993d8191d6b3317082efa2']='health.d/qos.conf' ['b7d769ce86a7aebba01315da5c0799e6']='health.d/ram.conf' ['b81b8f331161b0d48e03f6fbf6b6d062']='health.d/memcached.conf' @@ -322,19 +352,26 @@ declare -A configs_signatures=( ['ba11ea2d2f632b2de4b1224bcdc54f07']='python.d/smartd_log.conf' ['bb51112d01ff20053196a57632df8962']='apps_groups.conf' ['bba2f3886587f137ea08a6e63dd3d376']='python.d.conf' + ['bcd94c4fa2f89c710ff807de061ab11c']='health.d/net.conf' ['bda5517ea01640cfdfa0a27549619d6a']='health.d/memcached.conf' ['bf66f113b2dd8d8fb444cbd5650f284c']='health_alarm_notify.conf' ['c004430f55310ae9ed489c4905ed02cb']='charts.d/apache.conf' ['c080e006f544c949baca33cc24a9c126']='health_alarm_notify.conf' ['c1a7e634b5b8aad523a0d115a93379cd']='health.d/memcached.conf' ['c3296c08260bcd556e74711c820817be']='health.d/cpu.conf' + ['c3661b68232e06de90bb5e63e725b8b6']='health_alarm_notify.conf' ['c61948101e0e6846679682794ee48c5b']='python.d/nginx.conf' + ['c6403d8b1bcfa52d3abb941be155fc03']='python.d.conf' + ['c84fd3292710091802e443c8e688dee1']='health_alarm_notify.conf' ['c88fb430f35b7d8f08775d84debffbd2']='python.d/phpfpm.conf' + ['c94cb4f4eeaa13c1dcee6248deb01829']='python.d/postgres.conf' + ['c9a16df512b4a9ce7fa65f5a69bda20a']='python.d/web_log.conf' ['c9b792755de59d842ba95f8c315d94c8']='health.d/swap.conf' ['ca026d7c779f0a7cb7787713c5be5c47']='charts.d.conf' ['ca08a9b18d38ae0a0f5081a7cdc96863']='health.d/swap.conf' ['ca0eb92bdd3de67582ea6db37462895f']='health.d/tcp_resets.conf' ['ca249db7a0637d55abb938d969f9b486']='python.d/postfix.conf' + ['ca9e52b3ee3c71d3d042dc531753a1fd']='apps_groups.conf' ['cb178b15427274d7def5b14bc4c09441']='health.d/net.conf' ['cb60badf376d246ad8ec9d3f524db430']='health.d/disks.conf' ['cb7f80cd2768c649d7448e01f8aa6579']='python.d.conf' @@ -376,8 +413,12 @@ declare -A configs_signatures=( ['e0242003fd2e3f9ac1b9314e802ada79']='python.d/hddtemp.conf' ['e0ba3bc216ffc9933b4741dbb6b1f8c8']='health.d/web_log.conf' ['e0e96cc47ed61d6492416be5236cd4d3']='python.d/apache_cache.conf' + ['e0ffc0c34424b35666fddf7f61e05def']='health.d/tcp_resets.conf' + ['e1a8bf99d36683c10225100f207a2b59']='python.d/web_log.conf' ['e2f3388c06726154c10ec22bad5bc7ec']='fping.conf' ['e3023092e3b2bbb5351e0fe6682f4fe9']='health_alarm_notify.conf' + ['e3112d8e06fa77888aab02e8fcd22e25']='apps_groups.conf' + ['e3996f70a4b09315b4a64e3df7d34d43']='python.d/rabbitmq.conf' ['e3d100c2d0347c08efbf6245e05620c6']='python.d/fail2ban.conf' ['e3e5bc57335c489f01b8559f5c70e112']='python.d/squid.conf' ['e40947d22f7ed5359f12fc89e3512963']='python.d/dovecot.conf' @@ -390,12 +431,18 @@ declare -A configs_signatures=( ['eaa7beb935cae9c48a40fb934eb105a7']='health.d/web_log.conf' ['eb5168f0b516bc982aac45e59da6e52e']='health.d/nginx.conf' ['eb748d6fb69d11b0d29c5794657e206c']='health.d/qos.conf' + ['eb9fedc3c1dface77312d9bf48f673a8']='stream.conf' ['ebd0612ccc5807524ebb2b647e3e56c9']='apps_groups.conf' ['ecd3aa97e2581f88eb466d6612690ef2']='charts.d/nginx.conf' + ['ed43efac299c31f8fd5e2abccff30071']='python.d/samba.conf' + ['ed80e6b2cfc8b08adea7027fc03daa68']='python.d.conf' ['ee5343881744e6a97e6ee5cdd329cfb8']='health.d/retroshare.conf' ['ef1861bf5725d91e773cbdba05687597']='python.d.conf' ['ef9916ea144878a9f37cbb6b1b29da10']='health.d/squid.conf' + ['f075be84c5bfac7e34de2a091841360c']='statsd.d/example.conf' + ['f0a86c5bae3c4b32b266dacbf74ca4a3']='python.d/web_log.conf' ['f1446cb3f1a905ee06defa2aa15ee806']='python.d/web_log.conf' + ['f1682835e3414f60284c13bf1662e50f']='health.d/web_log.conf' ['f2f1b8656f5011e965ac45b818cf668d']='apps_groups.conf' ['f42df9f13abfae2426519c6728b34882']='charts.d/example.conf' ['f4c5d88c34d3fb853498124177cc77f1']='python.d.conf' @@ -410,6 +457,8 @@ declare -A configs_signatures=( ['f96acba4b14b0c1b50d0187a04416151']='health_alarm_notify.conf' ['f9be549a849d023595d19d5d74263e0f']='health.d/tcp_resets.conf' ['fa4396513b358d6ec6a7f5bfb08439b8']='health.d/net.conf' + ['fbdb6f5d3906d3d8ea4e28f6ba6965a6']='python.d/go_expvar.conf' + ['fc40b83f173bc4676d686867a8369a62']='python.d/dns_query_time.conf' ['fd3164e6e8cb6726706267eae49aa082']='health_alarm_notify.conf' ['fdd11640ba626cc2064c2fe3ea3eee4c']='health.d/cpu.conf' ['fde44f62c8d7e52f09705cd273fae6b1']='charts.d/tomcat.conf' diff --git a/configure b/configure index f638780b4..6a9d15b9c 100755 --- a/configure +++ b/configure @@ -1,11 +1,9 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for netdata 1.6.0. +# Generated by GNU Autoconf 2.69 for netdata 1.7.0. # # -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software -# Foundation, Inc. +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation @@ -134,6 +132,31 @@ export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh @@ -167,7 +190,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi -test x\$exitcode = x0 || exit 1" +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && @@ -212,21 +236,25 @@ IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : - # We cannot yet assume a decent shell, so we have to provide a - # neutralization value for shells without unset; and this also - # works around shells that cannot unset nonexistent variables. - # Preserve -v and -x to the replacement shell. - BASH_ENV=/dev/null - ENV=/dev/null - (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV - export CONFIG_SHELL - case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; - esac - exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 fi if test x$as_have_required = xno; then : @@ -328,6 +356,14 @@ $as_echo X"$as_dir" | } # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take @@ -449,6 +485,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). @@ -483,16 +523,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -504,28 +544,8 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -557,8 +577,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='netdata' PACKAGE_TARNAME='netdata' -PACKAGE_VERSION='1.6.0' -PACKAGE_STRING='netdata 1.6.0' +PACKAGE_VERSION='1.7.0' +PACKAGE_STRING='netdata 1.7.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -693,6 +713,10 @@ build_os build_vendor build_cpu build +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V am__untar am__tar AMTAR @@ -763,6 +787,7 @@ ac_subst_files='' ac_user_opts=' enable_option_checking enable_maintainer_mode +enable_silent_rules enable_dependency_tracking enable_plugin_nfacct enable_plugin_freeipmi @@ -1261,8 +1286,6 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1348,7 +1371,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures netdata 1.6.0 to adapt to many kinds of systems. +\`configure' configures netdata 1.7.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1418,7 +1441,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of netdata 1.6.0:";; + short | recursive ) echo "Configuration of netdata 1.7.0:";; esac cat <<\_ACEOF @@ -1426,10 +1449,15 @@ Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-maintainer-mode enable make rules and dependencies not useful - (and sometimes confusing) to the casual installer - --disable-dependency-tracking speeds up one-time build - --enable-dependency-tracking do not reject slow dependency extractors + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build --enable-plugin-nfacct enable nfacct plugin, requires root --enable-plugin-freeipmi enable freeipmi plugin @@ -1554,10 +1582,10 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -netdata configure 1.6.0 -generated by GNU Autoconf 2.68 +netdata configure 1.7.0 +generated by GNU Autoconf 2.69 -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1830,7 +1858,7 @@ $as_echo "$ac_try_echo"; } >&5 test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || - $as_test_x conftest$ac_exeext + test -x conftest$ac_exeext }; then : ac_retval=0 else @@ -1995,7 +2023,8 @@ int main () { static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; -test_array [0] = 0 +test_array [0] = 0; +return test_array [0]; ; return 0; @@ -2011,7 +2040,8 @@ main () { static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; -test_array [0] = 0 +test_array [0] = 0; +return test_array [0]; ; return 0; @@ -2068,7 +2098,8 @@ int main () { static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; -test_array [0] = 0 +test_array [0] = 0; +return test_array [0]; ; return 0; @@ -2160,7 +2191,8 @@ int main () { static int test_array [1 - 2 * !(($2) >= 0)]; -test_array [0] = 0 +test_array [0] = 0; +return test_array [0]; ; return 0; @@ -2176,7 +2208,8 @@ int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0 +test_array [0] = 0; +return test_array [0]; ; return 0; @@ -2202,7 +2235,8 @@ int main () { static int test_array [1 - 2 * !(($2) < 0)]; -test_array [0] = 0 +test_array [0] = 0; +return test_array [0]; ; return 0; @@ -2218,7 +2252,8 @@ int main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; -test_array [0] = 0 +test_array [0] = 0; +return test_array [0]; ; return 0; @@ -2252,7 +2287,8 @@ int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0 +test_array [0] = 0; +return test_array [0]; ; return 0; @@ -2324,8 +2360,8 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by netdata $as_me 1.6.0, which was -generated by GNU Autoconf 2.68. Invocation command line was +It was created by netdata $as_me 1.7.0, which was +generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2705,7 +2741,7 @@ $as_echo "$as_me: ***************** MAINTAINER MODE *****************" >&6;} PACKAGE_BUILT_DATE=$(date '+%d %b %Y') fi -PACKAGE_RPM_VERSION="1.6.0" +PACKAGE_RPM_VERSION="1.7.0" @@ -2746,7 +2782,7 @@ ac_config_headers="$ac_config_headers config.h" -am__api_version='1.11' +am__api_version='1.14' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or @@ -2785,7 +2821,7 @@ case $as_dir/ in #(( # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. @@ -2843,9 +2879,6 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } -# Just in case -sleep 1 -echo timestamp > conftest.file # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' @@ -2856,32 +2889,40 @@ case `pwd` in esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) - as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;; + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac -# Do `set' in a subshell so we don't clobber the current shell's +# Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( - set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` - if test "$*" = "X"; then - # -L didn't work. - set X `ls -t "$srcdir/configure" conftest.file` - fi - rm -f conftest.file - if test "$*" != "X $srcdir/configure conftest.file" \ - && test "$*" != "X conftest.file $srcdir/configure"; then - - # If neither matched, then we have a broken ls. This can happen - # if, for instance, CONFIG_SHELL is bash and it inherits a - # broken ls alias from the environment. This has actually - # happened. Such a system could not be considered "sane". - as_fn_error $? "ls -t appears to fail. Make sure there is not a broken -alias in your environment" "$LINENO" 5 - fi - + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done test "$2" = conftest.file ) then @@ -2893,6 +2934,16 @@ Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. @@ -2915,12 +2966,12 @@ if test x"${MISSING+set}" != xset; then esac fi # Use eval to expand $SHELL -if eval "$MISSING --run true"; then - am_missing_run="$MISSING --run " +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " else am_missing_run= - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5 -$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh}" != xset; then @@ -2932,10 +2983,10 @@ if test x"${install_sh}" != xset; then esac fi -# Installed binaries are usually stripped using `strip' when the user -# run `make install-strip'. However `strip' might not be the right +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake -# will honor the `STRIP' environment variable to overrule this program. +# will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. @@ -2954,7 +3005,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2994,7 +3045,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3045,7 +3096,7 @@ do test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do - { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ @@ -3074,12 +3125,6 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } -mkdir_p="$MKDIR_P" -case $mkdir_p in - [\\/$]* | ?:[\\/]*) ;; - */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; -esac - for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. @@ -3098,7 +3143,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3162,6 +3207,45 @@ else fi rmdir .tst 2>/dev/null +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." @@ -3184,7 +3268,7 @@ fi # Define the identity of the package. PACKAGE='netdata' - VERSION='1.6.0' + VERSION='1.7.0' cat >>confdefs.h <<_ACEOF @@ -3212,18 +3296,70 @@ AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + # We need awk for the "check" target. The system "awk" is bad on # some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 @@ -3317,7 +3453,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3357,7 +3493,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3410,7 +3546,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3451,7 +3587,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue @@ -3509,7 +3645,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3553,7 +3689,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3999,8 +4135,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include -#include -#include +struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -4084,6 +4219,65 @@ ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" @@ -4103,7 +4297,7 @@ am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf -# Ignore all kinds of additional output from `make'. +# Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include @@ -4159,8 +4353,8 @@ else # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named `D' -- because `-MD' means `put the output - # in D'. + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're @@ -4195,16 +4389,16 @@ else : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with - # Solaris 8's {/usr,}/bin/sh. - touch sub/conftst$i.h + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - # We check with `-c' and `-o' for the sake of the "dashmstdout" + # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly - # handle `-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in @@ -4213,8 +4407,8 @@ else test "$am__universal" = false || continue ;; nosideeffect) - # after this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else @@ -4222,7 +4416,7 @@ else fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok `-c -o', but also, the minuso test has + # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} @@ -4304,7 +4498,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4347,7 +4541,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4552,7 +4746,7 @@ do for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue + as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in @@ -4618,7 +4812,7 @@ do for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue + as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in @@ -4825,8 +5019,8 @@ else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -# define __EXTENSIONS__ 1 - $ac_includes_default +# define __EXTENSIONS__ 1 + $ac_includes_default int main () { @@ -5323,6 +5517,17 @@ _ACEOF fi done +for ac_func in recvmmsg +do : + ac_fn_c_check_func "$LINENO" "recvmmsg" "ac_cv_func_recvmmsg" +if test "x$ac_cv_func_recvmmsg" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_RECVMMSG 1 +_ACEOF + +fi +done + ac_fn_c_find_intX_t "$LINENO" "8" "ac_cv_c_int8_t" case $ac_cv_c_int8_t in #( @@ -5601,13 +5806,19 @@ int main (int argc, char **argv) { volatile unsigned long ul1 = 1, ul2 = 0, ul3 = 2; + __atomic_load_n(&ul1, __ATOMIC_SEQ_CST); __atomic_compare_exchange(&ul1, &ul2, &ul3, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); __atomic_fetch_add(&ul1, 1, __ATOMIC_SEQ_CST); __atomic_fetch_sub(&ul3, 1, __ATOMIC_SEQ_CST); + __atomic_or_fetch(&ul1, ul2, __ATOMIC_SEQ_CST); + __atomic_and_fetch(&ul1, ul2, __ATOMIC_SEQ_CST); volatile unsigned long long ull1 = 1, ull2 = 0, ull3 = 2; + __atomic_load_n(&ull1, __ATOMIC_SEQ_CST); __atomic_compare_exchange(&ull1, &ull2, &ull3, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); __atomic_fetch_add(&ull1, 1, __ATOMIC_SEQ_CST); __atomic_fetch_sub(&ull3, 1, __ATOMIC_SEQ_CST); + __atomic_or_fetch(&ull1, ull2, __ATOMIC_SEQ_CST); + __atomic_and_fetch(&ull1, ull2, __ATOMIC_SEQ_CST); return 0; } @@ -5770,6 +5981,7 @@ $as_echo_n "checking operating system... " >&6; } case "$host_os" in freebsd*) build_target=freebsd + CFLAGS="${CFLAGS} -I/usr/local/include" ;; darwin*) build_target=macos @@ -5982,7 +6194,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ax_pthread_config="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6174,7 +6386,7 @@ fi #handle absolute path differently from PATH based program lookup case "x$CC" in #( x/*) : - if { test -f ${CC}_r && $as_test_x ${CC}_r; }; then : + if as_fn_executable_p ${CC}_r; then : PTHREAD_CC="${CC}_r" fi ;; #( *) : @@ -6196,7 +6408,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_PTHREAD_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7149,7 +7361,113 @@ else IPMIMONITORING_LIBS=$pkg_cv_IPMIMONITORING_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - have_ipmimonitoring=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for + ipmi_monitoring_sensor_readings_by_record_id, + ipmi_monitoring_sensor_readings_by_sensor_type, + ipmi_monitoring_sensor_read_sensor_number, + ipmi_monitoring_sensor_read_sensor_name, + ipmi_monitoring_sensor_read_sensor_state, + ipmi_monitoring_sensor_read_sensor_units, + ipmi_monitoring_sensor_iterator_next, + ipmi_monitoring_ctx_sensor_config_file, + ipmi_monitoring_ctx_sdr_cache_directory, + ipmi_monitoring_ctx_errormsg, + ipmi_monitoring_ctx_create + in -lipmimonitoring" >&5 +$as_echo_n "checking for + ipmi_monitoring_sensor_readings_by_record_id, + ipmi_monitoring_sensor_readings_by_sensor_type, + ipmi_monitoring_sensor_read_sensor_number, + ipmi_monitoring_sensor_read_sensor_name, + ipmi_monitoring_sensor_read_sensor_state, + ipmi_monitoring_sensor_read_sensor_units, + ipmi_monitoring_sensor_iterator_next, + ipmi_monitoring_ctx_sensor_config_file, + ipmi_monitoring_ctx_sdr_cache_directory, + ipmi_monitoring_ctx_errormsg, + ipmi_monitoring_ctx_create + in -lipmimonitoring... " >&6; } +if ${ac_cv_lib_ipmimonitoring__________ipmi_monitoring_sensor_readings_by_record_id__________ipmi_monitoring_sensor_readings_by_sensor_type__________ipmi_monitoring_sensor_read_sensor_number__________ipmi_monitoring_sensor_read_sensor_name__________ipmi_monitoring_sensor_read_sensor_state__________ipmi_monitoring_sensor_read_sensor_units__________ipmi_monitoring_sensor_iterator_next__________ipmi_monitoring_ctx_sensor_config_file__________ipmi_monitoring_ctx_sdr_cache_directory__________ipmi_monitoring_ctx_errormsg__________ipmi_monitoring_ctx_create_____+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lipmimonitoring $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char + ipmi_monitoring_sensor_readings_by_record_id, + ipmi_monitoring_sensor_readings_by_sensor_type, + ipmi_monitoring_sensor_read_sensor_number, + ipmi_monitoring_sensor_read_sensor_name, + ipmi_monitoring_sensor_read_sensor_state, + ipmi_monitoring_sensor_read_sensor_units, + ipmi_monitoring_sensor_iterator_next, + ipmi_monitoring_ctx_sensor_config_file, + ipmi_monitoring_ctx_sdr_cache_directory, + ipmi_monitoring_ctx_errormsg, + ipmi_monitoring_ctx_create + (); +int +main () +{ +return + ipmi_monitoring_sensor_readings_by_record_id, + ipmi_monitoring_sensor_readings_by_sensor_type, + ipmi_monitoring_sensor_read_sensor_number, + ipmi_monitoring_sensor_read_sensor_name, + ipmi_monitoring_sensor_read_sensor_state, + ipmi_monitoring_sensor_read_sensor_units, + ipmi_monitoring_sensor_iterator_next, + ipmi_monitoring_ctx_sensor_config_file, + ipmi_monitoring_ctx_sdr_cache_directory, + ipmi_monitoring_ctx_errormsg, + ipmi_monitoring_ctx_create + (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ipmimonitoring__________ipmi_monitoring_sensor_readings_by_record_id__________ipmi_monitoring_sensor_readings_by_sensor_type__________ipmi_monitoring_sensor_read_sensor_number__________ipmi_monitoring_sensor_read_sensor_name__________ipmi_monitoring_sensor_read_sensor_state__________ipmi_monitoring_sensor_read_sensor_units__________ipmi_monitoring_sensor_iterator_next__________ipmi_monitoring_ctx_sensor_config_file__________ipmi_monitoring_ctx_sdr_cache_directory__________ipmi_monitoring_ctx_errormsg__________ipmi_monitoring_ctx_create_____=yes +else + ac_cv_lib_ipmimonitoring__________ipmi_monitoring_sensor_readings_by_record_id__________ipmi_monitoring_sensor_readings_by_sensor_type__________ipmi_monitoring_sensor_read_sensor_number__________ipmi_monitoring_sensor_read_sensor_name__________ipmi_monitoring_sensor_read_sensor_state__________ipmi_monitoring_sensor_read_sensor_units__________ipmi_monitoring_sensor_iterator_next__________ipmi_monitoring_ctx_sensor_config_file__________ipmi_monitoring_ctx_sdr_cache_directory__________ipmi_monitoring_ctx_errormsg__________ipmi_monitoring_ctx_create_____=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ipmimonitoring__________ipmi_monitoring_sensor_readings_by_record_id__________ipmi_monitoring_sensor_readings_by_sensor_type__________ipmi_monitoring_sensor_read_sensor_number__________ipmi_monitoring_sensor_read_sensor_name__________ipmi_monitoring_sensor_read_sensor_state__________ipmi_monitoring_sensor_read_sensor_units__________ipmi_monitoring_sensor_iterator_next__________ipmi_monitoring_ctx_sensor_config_file__________ipmi_monitoring_ctx_sdr_cache_directory__________ipmi_monitoring_ctx_errormsg__________ipmi_monitoring_ctx_create_____" >&5 +$as_echo "$ac_cv_lib_ipmimonitoring__________ipmi_monitoring_sensor_readings_by_record_id__________ipmi_monitoring_sensor_readings_by_sensor_type__________ipmi_monitoring_sensor_read_sensor_number__________ipmi_monitoring_sensor_read_sensor_name__________ipmi_monitoring_sensor_read_sensor_state__________ipmi_monitoring_sensor_read_sensor_units__________ipmi_monitoring_sensor_iterator_next__________ipmi_monitoring_ctx_sensor_config_file__________ipmi_monitoring_ctx_sdr_cache_directory__________ipmi_monitoring_ctx_errormsg__________ipmi_monitoring_ctx_create_____" >&6; } +if test "x$ac_cv_lib_ipmimonitoring__________ipmi_monitoring_sensor_readings_by_record_id__________ipmi_monitoring_sensor_readings_by_sensor_type__________ipmi_monitoring_sensor_read_sensor_number__________ipmi_monitoring_sensor_read_sensor_name__________ipmi_monitoring_sensor_read_sensor_state__________ipmi_monitoring_sensor_read_sensor_units__________ipmi_monitoring_sensor_iterator_next__________ipmi_monitoring_ctx_sensor_config_file__________ipmi_monitoring_ctx_sdr_cache_directory__________ipmi_monitoring_ctx_errormsg__________ipmi_monitoring_ctx_create_____" = xyes; then : + ac_fn_c_check_header_mongrel "$LINENO" "ipmi_monitoring.h" "ac_cv_header_ipmi_monitoring_h" "$ac_includes_default" +if test "x$ac_cv_header_ipmi_monitoring_h" = xyes; then : + ac_fn_c_check_header_mongrel "$LINENO" "ipmi_monitoring_bitmasks.h" "ac_cv_header_ipmi_monitoring_bitmasks_h" "$ac_includes_default" +if test "x$ac_cv_header_ipmi_monitoring_bitmasks_h" = xyes; then : + have_ipmimonitoring=yes +else + have_ipmimonitoring=no + +fi + + +else + have_ipmimonitoring=no + +fi + + +else + have_ipmimonitoring=no + +fi + fi test "${enable_plugin_freeipmi}" = "yes" -a "${have_ipmimonitoring}" != "yes" && \ as_fn_error $? "ipmimonitoring required but not found. Try installing 'libipmimonitoring-dev' or 'libipmimonitoring-devel'" "$LINENO" 5 @@ -7624,6 +7942,14 @@ if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' @@ -7966,16 +8292,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -8035,28 +8361,16 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -8077,8 +8391,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by netdata $as_me 1.6.0, which was -generated by GNU Autoconf 2.68. Invocation command line was +This file was extended by netdata $as_me 1.7.0, which was +generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -8143,11 +8457,11 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -netdata config.status 1.6.0 -configured by $0, generated by GNU Autoconf 2.68, +netdata config.status 1.7.0 +configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -8238,7 +8552,7 @@ fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then - set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' @@ -8881,7 +9195,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { - # Autoconf 2.62 quotes --file arguments for eval, but not when files + # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in @@ -8894,7 +9208,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named `Makefile.in', but + # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. @@ -8928,21 +9242,19 @@ $as_echo X"$mf" | continue fi # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running `make'. + # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "am__include" && continue + test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # When using ansi2knr, U may be empty or an underscore; expand it - U=`sed -n 's/^U = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || diff --git a/configure.ac b/configure.ac index 2c83830ca..010e0e939 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_PREREQ(2.60) define([VERSION_MAJOR], [1]) -define([VERSION_MINOR], [6]) +define([VERSION_MINOR], [7]) define([VERSION_FIX], [0]) define([VERSION_NUMBER], VERSION_MAJOR[.]VERSION_MINOR[.]VERSION_FIX) define([VERSION_SUFFIX], []) @@ -126,6 +126,7 @@ AC_CHECK_TYPES([struct timespec, clockid_t], [], [], [[#include ]]) AC_SEARCH_LIBS([clock_gettime], [rt posix4]) AC_CHECK_FUNCS([clock_gettime]) AC_CHECK_FUNCS([sched_setscheduler sched_get_priority_min sched_get_priority_max nice]) +AC_CHECK_FUNCS([recvmmsg]) AC_TYPE_INT8_T AC_TYPE_INT16_T @@ -155,6 +156,7 @@ AC_MSG_CHECKING([operating system]) case "$host_os" in freebsd*) build_target=freebsd + CFLAGS="${CFLAGS} -I/usr/local/include" ;; darwin*) build_target=macos @@ -341,7 +343,30 @@ AM_CONDITIONAL([ENABLE_PLUGIN_APPS], [test "${enable_plugin_apps}" = "yes"]) PKG_CHECK_MODULES( [IPMIMONITORING], [libipmimonitoring], - [have_ipmimonitoring=yes], + [AC_CHECK_LIB([ipmimonitoring], [ + ipmi_monitoring_sensor_readings_by_record_id, + ipmi_monitoring_sensor_readings_by_sensor_type, + ipmi_monitoring_sensor_read_sensor_number, + ipmi_monitoring_sensor_read_sensor_name, + ipmi_monitoring_sensor_read_sensor_state, + ipmi_monitoring_sensor_read_sensor_units, + ipmi_monitoring_sensor_iterator_next, + ipmi_monitoring_ctx_sensor_config_file, + ipmi_monitoring_ctx_sdr_cache_directory, + ipmi_monitoring_ctx_errormsg, + ipmi_monitoring_ctx_create + ], + [AC_CHECK_HEADER( + [ipmi_monitoring.h], + [AC_CHECK_HEADER( + [ipmi_monitoring_bitmasks.h], + [have_ipmimonitoring=yes], + [have_ipmimonitoring=no] + )], + [have_ipmimonitoring=no] + )], + [have_ipmimonitoring=no] + )], [have_ipmimonitoring=no] ) test "${enable_plugin_freeipmi}" = "yes" -a "${have_ipmimonitoring}" != "yes" && \ diff --git a/contrib/Makefile.am b/contrib/Makefile.am index b58b40e26..15e9c0008 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -21,6 +21,7 @@ dist_noinst_DATA = \ dist_noinst_SCRIPTS = \ debian/netdata.init \ + nc-backend.sh \ $(NULL) debian/changelog: diff --git a/contrib/Makefile.in b/contrib/Makefile.in index 29db296aa..a10dfa972 100644 --- a/contrib/Makefile.in +++ b/contrib/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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. @@ -17,6 +16,51 @@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -36,8 +80,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = contrib -DIST_COMMON = $(dist_noinst_DATA) $(dist_noinst_SCRIPTS) \ - $(srcdir)/Makefile.am $(srcdir)/Makefile.in +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(dist_noinst_SCRIPTS) $(dist_noinst_DATA) 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_lto.m4 \ @@ -54,12 +98,31 @@ CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = SCRIPTS = $(dist_noinst_SCRIPTS) +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 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac DATA = $(dist_noinst_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -223,6 +286,7 @@ dist_noinst_DATA = \ dist_noinst_SCRIPTS = \ debian/netdata.init \ + nc-backend.sh \ $(NULL) all: all-am @@ -258,11 +322,11 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -tags: TAGS -TAGS: +tags TAGS: + +ctags CTAGS: -ctags: CTAGS -CTAGS: +cscope cscopelist: distdir: $(DISTFILES) @@ -398,15 +462,16 @@ uninstall-am: .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am 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-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am +.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ + ctags-am distclean distclean-generic distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am 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-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am tags-am uninstall uninstall-am debian/changelog: diff --git a/contrib/debian/changelog b/contrib/debian/changelog new file mode 100644 index 000000000..bfb070549 --- /dev/null +++ b/contrib/debian/changelog @@ -0,0 +1,3 @@ +netdata (1.7.0) UNRELEASED; urgency=medium + * Latest release + -- Netdata Team <> Sun, 16 Jul 2017 19:28:33 +0000 diff --git a/contrib/debian/compat b/contrib/debian/compat new file mode 100644 index 000000000..ec635144f --- /dev/null +++ b/contrib/debian/compat @@ -0,0 +1 @@ +9 diff --git a/contrib/debian/control b/contrib/debian/control new file mode 100644 index 000000000..24c5f4c4b --- /dev/null +++ b/contrib/debian/control @@ -0,0 +1,25 @@ +Source: netdata +Build-Depends: debhelper (>= 9), + dh-autoreconf, + dh-systemd (>= 1.5), + dpkg-dev (>= 1.13.19), + zlib1g-dev, + uuid-dev +Section: net +Priority: optional +Maintainer: Costa Tsaousis +Standards-Version: 3.9.6 +Homepage: https://github.com/firehol/netdata/wiki + +Package: netdata +Architecture: any +Depends: adduser, + libcap2-bin (>= 1:2.0), + lsb-base (>= 3.1-23.2), + ${misc:Depends}, + ${shlibs:Depends} +Description: real-time charts for system monitoring + Netdata is a daemon that collects data in realtime (per second) + and presents a web site to view and analyze them. The presentation + is also real-time and full of interactive charts that precisely + render all collected values. diff --git a/contrib/debian/control.wheezy b/contrib/debian/control.wheezy new file mode 100644 index 000000000..4103908a2 --- /dev/null +++ b/contrib/debian/control.wheezy @@ -0,0 +1,25 @@ +Source: netdata +Build-Depends: debhelper (>= 9), + dh-autoreconf, + pkg-config, + dpkg-dev (>= 1.13.19), + zlib1g-dev, + uuid-dev +Section: net +Priority: optional +Maintainer: Costa Tsaousis +Standards-Version: 3.9.6 +Homepage: https://github.com/firehol/netdata/wiki + +Package: netdata +Architecture: any +Depends: adduser, + libcap2-bin (>= 1:2.0), + lsb-base (>= 3.1-23.2), + ${misc:Depends}, + ${shlibs:Depends} +Description: real-time charts for system monitoring + Netdata is a daemon that collects data in realtime (per second) + and presents a web site to view and analyze them. The presentation + is also real-time and full of interactive charts that precisely + render all collected values. diff --git a/contrib/debian/copyright b/contrib/debian/copyright new file mode 100644 index 000000000..11a3d6390 --- /dev/null +++ b/contrib/debian/copyright @@ -0,0 +1,10 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Netdata +Upstream-Contact: Costa Tsaousis +Source: https://github.com/firehol/netdata + +Files: * +Copyright: 2014-2016, Costa Tsaousis +License: GPL-3+ + On Debian systems, the complete text of the GNU General Public + License version 3 can be found in /usr/share/common-licenses/GPL-3. diff --git a/contrib/debian/netdata.conf b/contrib/debian/netdata.conf new file mode 100644 index 000000000..a963d80b7 --- /dev/null +++ b/contrib/debian/netdata.conf @@ -0,0 +1,16 @@ +# NetData Configuration + +# The current full configuration can be retrieved from the running +# server at the URL +# +# http://localhost:19999/netdata.conf +# +# for example: +# +# wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf +# + +[global] + run as user = netdata + web files owner = root + web files group = netdata diff --git a/contrib/debian/netdata.default b/contrib/debian/netdata.default new file mode 100644 index 000000000..9e7f8ae6e --- /dev/null +++ b/contrib/debian/netdata.default @@ -0,0 +1,5 @@ +# Extra arguments to pass to netdata +# +#EXTRA_OPTS="" +#uncomment following line if you are building a wheezy-package +#EXTRA_OPTS="-P /var/run/netdata.pid" diff --git a/contrib/debian/netdata.docs b/contrib/debian/netdata.docs new file mode 100644 index 000000000..56631abf1 --- /dev/null +++ b/contrib/debian/netdata.docs @@ -0,0 +1 @@ +ChangeLog diff --git a/contrib/debian/netdata.init b/contrib/debian/netdata.init new file mode 100755 index 000000000..c1b2b74de --- /dev/null +++ b/contrib/debian/netdata.init @@ -0,0 +1,56 @@ +#!/bin/sh +# Start/stop the netdata daemon. +# +### BEGIN INIT INFO +# Provides: netdata +# Required-Start: $remote_fs +# Required-Stop: $remote_fs +# Should-Start: $network +# Should-Stop: $network +# Default-Start: 2 3 4 5 +# Default-Stop: +# Short-Description: Real-time charts for system monitoring +# Description: Netdata is a daemon that collects data in realtime (per second) +# and presents a web site to view and analyze them. The presentation +# is also real-time and full of interactive charts that precisely +# render all collected values. +### END INIT INFO + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +DESC="netdata daemon" +NAME=netdata +DAEMON=/usr/sbin/netdata +PIDFILE=/var/run/netdata/netdata.pid +SCRIPTNAME=/etc/init.d/"$NAME" + +test -f $DAEMON || exit 0 + +. /lib/lsb/init-functions + +[ -r /etc/default/netdata ] && . /etc/default/netdata + +case "$1" in +start) log_daemon_msg "Starting real-time system monitoring" "netdata" + start_daemon -p $PIDFILE $DAEMON $EXTRA_OPTS + log_end_msg $? + ;; +stop) log_daemon_msg "Stopping real-time system monitoring" "netdata" + killproc -p $PIDFILE $DAEMON + RETVAL=$? + [ $RETVAL -eq 0 ] && [ -e "$PIDFILE" ] && rm -f $PIDFILE + log_end_msg $RETVAL + # wait for plugins to exit + sleep 1 + ;; +restart|force-reload) log_daemon_msg "Restarting real-time system monitoring" "netdata" + $0 stop + $0 start + ;; +status) + status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $? + ;; +*) log_action_msg "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" + exit 2 + ;; +esac +exit 0 diff --git a/contrib/debian/netdata.install b/contrib/debian/netdata.install new file mode 100644 index 000000000..45d42b635 --- /dev/null +++ b/contrib/debian/netdata.install @@ -0,0 +1 @@ +debian/netdata.conf /etc/netdata/ diff --git a/contrib/debian/netdata.lintian-overrides b/contrib/debian/netdata.lintian-overrides new file mode 100644 index 000000000..45b2d868f --- /dev/null +++ b/contrib/debian/netdata.lintian-overrides @@ -0,0 +1,16 @@ + +# See Debian policy 10.9. apps.plugin has extra capabilities, so don't let +# normal users run it. +netdata: non-standard-executable-perm usr/lib/*/netdata/plugins.d/apps.plugin 0754 != 0755 + + +# FontAwesome is at least in the fonts-font-awesome package, but this is +# not available in wheezy. glyphicons-halflings-regular isn't currently in +# a Debian package. Therefore don't complain about shipping them with netdata +# for the time being. +netdata: duplicate-font-file usr/share/netdata/fonts/* +netdata: font-in-non-font-package usr/share/netdata/fonts/* + +# Files here are marked as conffiles so that local updates to the html files +# isn't clobbered on upgrade. +netdata: non-etc-file-marked-as-conffile var/lib/netdata/www/* diff --git a/contrib/debian/netdata.postinst.in b/contrib/debian/netdata.postinst.in new file mode 100644 index 000000000..29615f541 --- /dev/null +++ b/contrib/debian/netdata.postinst.in @@ -0,0 +1,41 @@ +#! /bin/sh + +set -e + +case "$1" in + configure) + if [ -z "$2" ]; then + if ! getent group netdata >/dev/null; then + addgroup --quiet --system netdata + fi + + if ! getent passwd netdata >/dev/null; then + adduser --quiet --system --ingroup netdata --home /var/lib/netdata --no-create-home netdata + fi + + if ! dpkg-statoverride --list /var/lib/netdata >/dev/null 2>&1; then + dpkg-statoverride --update --add netdata netdata 0755 /var/lib/netdata + fi + + if ! dpkg-statoverride --list /var/lib/netdata/www >/dev/null 2>&1; then + dpkg-statoverride --update --add root netdata 0755 /var/lib/netdata/www + fi + + if ! dpkg-statoverride --list /var/cache/netdata >/dev/null 2>&1; then + dpkg-statoverride --update --add netdata netdata 0755 /var/cache/netdata + fi + + fi + + dpkg-statoverride --update --add --force root netdata 0775 /var/lib/netdata/registry + chown -R root:netdata /usr/share/netdata/* + chown -R root:netdata /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d + setcap cap_dac_read_search,cap_sys_ptrace+ep /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d/apps.plugin + +#PERMS# + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/contrib/debian/netdata.postrm b/contrib/debian/netdata.postrm new file mode 100644 index 000000000..4ab4eeadd --- /dev/null +++ b/contrib/debian/netdata.postrm @@ -0,0 +1,43 @@ +#! /bin/sh + +set -e + +case "$1" in + remove) + ;; + + purge) + if dpkg-statoverride --list | grep -qw /var/cache/netdata; then + dpkg-statoverride --remove /var/cache/netdata + fi + + if dpkg-statoverride --list | grep -qw /var/lib/netdata/www; then + dpkg-statoverride --remove /var/lib/netdata/www + fi + + if dpkg-statoverride --list | grep -qw /var/lib/netdata; then + dpkg-statoverride --remove /var/lib/netdata + fi + + if getent passwd netdata >/dev/null; then + if [ -x /usr/sbin/deluser ]; then + deluser --quiet --system netdata || echo "Unable to remove netdata user" + fi + fi + + if getent group netdata >/dev/null; then + if [ -x /usr/sbin/delgroup ]; then + delgroup --quiet --system netdata || echo "Unable to remove netdata group" + fi + fi + + ;; + + *) + ;; +esac + +#DEBHELPER# + +exit 0 + diff --git a/contrib/debian/netdata.service b/contrib/debian/netdata.service new file mode 100644 index 000000000..e5d3a3863 --- /dev/null +++ b/contrib/debian/netdata.service @@ -0,0 +1,14 @@ +[Unit] +Description=netdata real-time system monitoring +After=network.target + +[Service] +Type=simple +EnvironmentFile=-/etc/default/netdata +ExecStart=/usr/sbin/netdata -D $EXTRA_OPTS +TimeoutStopSec=30 +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/contrib/debian/rules b/contrib/debian/rules new file mode 100755 index 000000000..ec4ec4182 --- /dev/null +++ b/contrib/debian/rules @@ -0,0 +1,87 @@ +#!/usr/bin/make -f + +# Find the arch we are building for, as this determines +# the location of plugins in /usr/lib +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) +TOP = $(CURDIR)/debian/netdata + +%: + # For jessie and beyond + # + dh $@ --with autoreconf,systemd + + # For wheezy or other non-systemd distributions use the following. You + # should also see contrib/README.md which gives details of updates to + # make to debian/control. + # + #dh $@ --with autoreconf + +override_dh_auto_configure: + dh_auto_configure -- --with-math --with-webdir=/var/lib/netdata/www + +debian/%.postinst: debian/%.postinst.in + sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' $< > $@ + +override_dh_install: debian/netdata.postinst + dh_install + + # Remove unneeded .keep files + # + find "$(TOP)" -name .keep -exec rm '{}' ';' + + # Move files that local user shouldn't be editing to /usr/share/netdata + # + mkdir -p "$(TOP)/usr/share/netdata" + for D in $$(find "$(TOP)/var/lib/netdata/www/" -maxdepth 1 -type d -printf '%f '); do \ + echo Relocating $$D; \ + mv "$(TOP)/var/lib/netdata/www/$$D" "$(TOP)/usr/share/netdata/$$D"; \ + ln -s "/usr/share/netdata/$$D" "$(TOP)/var/lib/netdata/www/$$D"; \ + done + + # Update postinst to set correct group for www files on installation. + # Should probably be dpkg-statoverride really, but that gets *really* + # messy. We also set all web files in /var as conffiles so an upgrade + # doesn't splat them. + # + for D in $$(find "$(TOP)/var/lib/netdata/www/" -maxdepth 1 -type f -printf '%f '); do \ + echo Updating postinst for $$D; \ + sed -i "s/^#PERMS#/chgrp netdata \/var\/lib\/netdata\/www\/$$D\n#PERMS#/g" \ + $(CURDIR)/debian/netdata.postinst; \ + echo "/var/lib/netdata/www/$$D" >> $(CURDIR)/debian/netdata.conffiles; \ + done + sed -i "/^#PERMS#/d" $(CURDIR)/debian/netdata.postinst + +override_dh_installdocs: + dh_installdocs + + # Docs should not be under /usr/lib + # + mv $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/plugins.d/README.md \ + $(TOP)/usr/share/doc/netdata/README.plugins.md + mv $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/charts.d/README.md \ + $(TOP)/usr/share/doc/netdata/README.charts.md + + # This doc is currently empty, so no point installing it. + # + rm $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/node.d/README.md + +override_dh_fixperms: + dh_fixperms + + # apps.plugin should only be runnable by the netdata user. It will be + # given extra capabilities in the postinst script. + # + chmod 0754 $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/plugins.d/apps.plugin + +override_dh_installlogrotate: + cp system/netdata.logrotate debian/netdata.logrotate + dh_installlogrotate + +override_dh_clean: + dh_clean + + # Tidy up copied/generated files + # + -[ -r $(CURDIR)/debian/netdata.logrotate ] && rm $(CURDIR)/debian/netdata.logrotate + -[ -r $(CURDIR)/debian/netdata.postinst ] && rm $(CURDIR)/debian/netdata.postinst + -[ -r $(CURDIR)/debian/netdata.conffiles ] && rm $(CURDIR)/debian/netdata.conffiles diff --git a/contrib/debian/source/format b/contrib/debian/source/format new file mode 100644 index 000000000..89ae9db8f --- /dev/null +++ b/contrib/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/contrib/nc-backend.sh b/contrib/nc-backend.sh new file mode 100755 index 000000000..aac5c20bb --- /dev/null +++ b/contrib/nc-backend.sh @@ -0,0 +1,151 @@ +#!/usr/bin/env bash + +MODE="${1}" +MY_PORT="${2}" +BACKEND_HOST="${3}" +BACKEND_PORT="${4}" +FILE="${NETDATA_NC_BACKEND_DIR-/tmp}/netdata-nc-backend-${MY_PORT}" + +log() { + logger --stderr --id=$$ --tag "netdata-nc-backend" "${*}" +} + +mync() { + local ret + + log "Running: nc ${*}" + nc "${@}" + ret=$? + + log "nc stopped with return code ${ret}." + + return ${ret} +} + +listen_save_replay_forever() { + local file="${1}" port="${2}" real_backend_host="${3}" real_backend_port="${4}" ret delay=1 started ended + + while [ 1 ] + do + log "Starting nc to listen on port ${port} and save metrics to ${file}" + + started=$(date +%s) + mync -l -p "${port}" | tee -a -p --output-error=exit "${file}" + ended=$(date +%s) + + if [ -s "${file}" ] + then + if [ ! -z "${real_backend_host}" -a ! -z "${real_backend_port}" ] + then + log "Attempting to send the metrics to the real backend at ${real_backend_host}:${real_backend_port}" + + mync "${real_backend_host}" "${real_backend_port}" <"${file}" + ret=$? + + if [ ${ret} -eq 0 ] + then + log "Successfuly sent the metrics to ${real_backend_host}:${real_backend_port}" + mv "${file}" "${file}.old" + touch "${file}" + else + log "Failed to send the metrics to ${real_backend_host}:${real_backend_port} (nc returned ${ret}) - appending more data to ${file}" + fi + else + log "No backend configured - appending more data to ${file}" + fi + fi + + # prevent a CPU hungry infinite loop + # if nc cannot listen to port + if [ $((ended - started)) -lt 5 ] + then + log "nc has been stopped too fast." + delay=30 + else + delay=1 + fi + + log "Waiting ${delay} seconds before listening again for data." + sleep ${delay} + done +} + +if [ "${MODE}" = "start" ] + then + + # start the listener, in exclusive mode + # only one can use the same file/port at a time + { + flock -n 9 + if [ $? -ne 0 ] + then + log "Cannot get exclusive lock on file ${FILE}.lock - Am I running multiple times?" + exit 2 + fi + + # save our PID to the lock file + echo "$$" >"${FILE}.lock" + + listen_save_replay_forever "${FILE}" ${MY_PORT} ${BACKEND_HOST} ${BACKEND_PORT} + ret=$? + + log "listener exited." + exit ${ret} + + } 9>>"${FILE}.lock" + + # we can only get here if ${FILE}.lock cannot be created + log "Cannot create file ${FILE}." + exit 3 + +elif [ "${MODE}" = "stop" ] + then + + { + flock -n 9 + if [ $? -ne 0 ] + then + pid=$(<${FILE}.lock) + log "Killing process ${pid}..." + kill -TERM -${pid} + exit 0 + fi + + log "File ${FILE}.lock has been locked by me but it shouldn't. Is a collector running?" + exit 4 + + } 9<"${FILE}.lock" + + log "File ${FILE}.lock does not exist. Is a collector running?" + exit 5 + +else + + cat <&2 - exit 1; - ;; + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] @@ -40,8 +39,8 @@ as side-effects. Environment variables: depmode Dependency tracking mode. - source Source file read by `PROGRAMS ARGS'. - object Object file output by `PROGRAMS ARGS'. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. @@ -57,6 +56,66 @@ EOF ;; esac +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 @@ -69,6 +128,9 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" +# Avoid interferences from the environment. +gccflag= dashmflag= + # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case @@ -80,26 +142,32 @@ if test "$depmode" = hp; then fi if test "$depmode" = dashXmstdout; then - # This is just like dashmstdout with a different argument. - dashmflag=-xM - depmode=dashmstdout + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then - # This is just like msvisualcpp but w/o cygpath translation. - # Just convert the backslash-escaped backslashes to single forward - # slashes to satisfy depend.m4 - cygpath_u='sed s,\\\\,/,g' - depmode=msvisualcpp + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then - # This is just like msvc7 but w/o cygpath translation. - # Just convert the backslash-escaped backslashes to single forward - # slashes to satisfy depend.m4 - cygpath_u='sed s,\\\\,/,g' - depmode=msvc7 + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc fi case "$depmode" in @@ -122,8 +190,7 @@ gcc3) done "$@" stat=$? - if test $stat -eq 0; then : - else + if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi @@ -131,13 +198,17 @@ gcc3) ;; gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like -## -MM, not -M (despite what the docs say). +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then @@ -145,33 +216,31 @@ gcc) fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? - if test $stat -eq 0; then : - else + if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" - alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz -## The second -e expression handles DOS-style file names with drive letters. + # The second -e expression handles DOS-style file names with drive + # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" -## This next piece of magic avoids the `deleted header file' problem. +## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. - tr ' ' ' -' < "$tmpdepfile" | -## Some versions of gcc put a space before the `:'. On the theory +## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. - sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ - | sed -e 's/$/ :/' >> "$depfile" + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; @@ -189,8 +258,7 @@ sgi) "$@" -MDupdate "$tmpdepfile" fi stat=$? - if test $stat -eq 0; then : - else + if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi @@ -198,43 +266,41 @@ sgi) if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" - # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; - # the IRIX cc adds comments like `#:fec' to the end of the + # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. - tr ' ' ' -' < "$tmpdepfile" \ - | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ - tr ' -' ' ' >> "$depfile" + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" - # The second pass generates a dummy entry for each header file. - tr ' ' ' -' < "$tmpdepfile" \ - | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ - >> "$depfile" + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" else - # The sourcefile does not contain any dependencies, so just - # store a dummy comment line, to avoid errors with the Makefile - # "include basename.Plo" scheme. - echo "#dummy" > "$depfile" + make_dummy_depfile fi rm -f "$tmpdepfile" ;; +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the - # current directory. Also, the AIX compiler puts `$object:' at the + # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. - dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` - test "x$dir" = "x$object" && dir= - base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + set_dir_from "$object" + set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u @@ -247,9 +313,7 @@ aix) "$@" -M fi stat=$? - - if test $stat -eq 0; then : - else + if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi @@ -258,44 +322,100 @@ aix) do test -f "$tmpdepfile" && break done - if test -f "$tmpdepfile"; then - # Each line is of the form `foo.o: dependent.h'. - # Do two passes, one to just change these to - # `$object: dependent.h' and one to simply `dependent.h:'. - sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" - # That's a tab and a space in the []. - sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" - else - # The sourcefile does not contain any dependencies, so just - # store a dummy comment line, to avoid errors with the Makefile - # "include basename.Plo" scheme. - echo "#dummy" > "$depfile" + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; -icc) - # Intel's C compiler understands `-MD -MF file'. However on - # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c - # ICC 7.0 will fill foo.d with something like - # foo.o: sub/foo.c - # foo.o: sub/foo.h - # which is wrong. We want: - # sub/foo.o: sub/foo.c - # sub/foo.o: sub/foo.h - # sub/foo.c: - # sub/foo.h: - # ICC 7.1 will output +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h - # and will wrap long lines using \ : + # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi - "$@" -MD -MF "$tmpdepfile" - stat=$? - if test $stat -eq 0; then : - else + if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi @@ -307,8 +427,8 @@ icc) sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. - sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | - sed -e 's/$/ :/' >> "$depfile" + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; @@ -319,9 +439,8 @@ hp2) # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. - dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` - test "x$dir" = "x$object" && dir= - base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + set_dir_from "$object" + set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d @@ -332,8 +451,7 @@ hp2) "$@" +Maked fi stat=$? - if test $stat -eq 0; then : - else + if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi @@ -343,77 +461,61 @@ hp2) test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then - sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" - # Add `dependent.h:' lines. + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. sed -ne '2,${ - s/^ *// - s/ \\*$// - s/$/:/ - p - }' "$tmpdepfile" >> "$depfile" + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" else - echo "#dummy" > "$depfile" + make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) - # The Tru64 compiler uses -MD to generate dependencies as a side - # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. - # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put - # dependencies in `foo.d' instead, so we check for that too. - # Subdirectories are respected. - dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` - test "x$dir" = "x$object" && dir= - base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` - - if test "$libtool" = yes; then - # With Tru64 cc, shared objects can also be used to make a - # static library. This mechanism is used in libtool 1.4 series to - # handle both shared and static libraries in a single compilation. - # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. - # - # With libtool 1.5 this exception was removed, and libtool now - # generates 2 separate objects for the 2 libraries. These two - # compilations output dependencies in $dir.libs/$base.o.d and - # in $dir$base.o.d. We have to check for both files, because - # one of the two compilations can be disabled. We should prefer - # $dir$base.o.d over $dir.libs/$base.o.d because the latter is - # automatically cleaned when .libs/ is deleted, while ignoring - # the former would cause a distcleancheck panic. - tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 - tmpdepfile2=$dir$base.o.d # libtool 1.5 - tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 - tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 - "$@" -Wc,-MD - else - tmpdepfile1=$dir$base.o.d - tmpdepfile2=$dir$base.d - tmpdepfile3=$dir$base.d - tmpdepfile4=$dir$base.d - "$@" -MD - fi - - stat=$? - if test $stat -eq 0; then : - else - rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" - exit $stat - fi - - for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" - do - test -f "$tmpdepfile" && break - done - if test -f "$tmpdepfile"; then - sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" - # That's a tab and a space in the []. - sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" - else - echo "#dummy" > "$depfile" - fi - rm -f "$tmpdepfile" - ;; + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; msvc7) if test "$libtool" = yes; then @@ -424,8 +526,7 @@ msvc7) "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" - if test "$stat" = 0; then : - else + if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi @@ -443,14 +544,15 @@ msvc7) p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g -s/\(.*\)/ \1 \\/p +s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { - s/.*/ / + s/.*/'"$tab"'/ G p }' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; @@ -478,7 +580,7 @@ dashmstdout) shift fi - # Remove `-o $object'. + # Remove '-o $object'. IFS=" " for arg do @@ -498,18 +600,18 @@ dashmstdout) done test -z "$dashmflag" && dashmflag=-M - # Require at least two characters before searching for `:' + # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: - # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | - sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" - tr ' ' ' -' < "$tmpdepfile" | \ -## Some versions of the HPUX 10.20 sed can't process this invocation -## correctly. Breaking it into two sed invocations is a workaround. - sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; @@ -562,11 +664,12 @@ makedepend) # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" - sed '1,2d' "$tmpdepfile" | tr ' ' ' -' | \ -## Some versions of the HPUX 10.20 sed can't process this invocation -## correctly. Breaking it into two sed invocations is a workaround. - sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; @@ -583,7 +686,7 @@ cpp) shift fi - # Remove `-o $object'. + # Remove '-o $object'. IFS=" " for arg do @@ -602,10 +705,10 @@ cpp) esac done - "$@" -E | - sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ - -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | - sed '$ s: \\$::' > "$tmpdepfile" + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" @@ -637,23 +740,23 @@ msvisualcpp) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") - set fnord "$@" - shift - shift - ;; + set fnord "$@" + shift + shift + ;; *) - set fnord "$@" "$arg" - shift - shift - ;; + set fnord "$@" "$arg" + shift + shift + ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" - sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" - echo " " >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; diff --git a/diagrams/netdata-overview.xml b/diagrams/netdata-overview.xml new file mode 100644 index 000000000..d8a0ab1bf --- /dev/null +++ b/diagrams/netdata-overview.xml @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/install-sh b/install-sh index a9244eb07..377bb8687 100755 --- a/install-sh +++ b/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2011-01-19.21; # UTC +scriptversion=2011-11-20.07; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -35,7 +35,7 @@ scriptversion=2011-01-19.21; # UTC # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it +# 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written @@ -156,7 +156,7 @@ while test $# -ne 0; do -s) stripcmd=$stripprog;; -t) dst_arg=$2 - # Protect names problematic for `test' and other utilities. + # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac @@ -190,7 +190,7 @@ if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then fi shift # arg dst_arg=$arg - # Protect names problematic for `test' and other utilities. + # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac @@ -202,7 +202,7 @@ if test $# -eq 0; then echo "$0: no input file specified." >&2 exit 1 fi - # It's OK to call `install-sh -d' without argument. + # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi @@ -240,7 +240,7 @@ fi for src do - # Protect names problematic for `test' and other utilities. + # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac @@ -354,7 +354,7 @@ do if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writeable bit of parent directory when it shouldn't. + # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in diff --git a/installer/functions.sh b/installer/functions.sh index 36d10ec70..c847ba1cd 100644 --- a/installer/functions.sh +++ b/installer/functions.sh @@ -1,5 +1,9 @@ # no shebang necessary - this is a library to be sourced +# make sure we have a UID +[ -z "${UID}" ] && UID="$(id -u)" + + # ----------------------------------------------------------------------------- # checking the availability of commands @@ -81,7 +85,7 @@ setup_terminal() { return 0 } -setup_terminal +setup_terminal || echo >/dev/null progress() { echo >&2 " --- ${TPUT_DIM}${TPUT_BOLD}${*}${TPUT_RESET} --- " @@ -120,13 +124,13 @@ systemctl_cmd="$(which_cmd systemctl)" service() { local cmd="${1}" action="${2}" - if [ ! -z "${service_cmd}" ] + if [ ! -z "${systemctl_cmd}" ] then - run "${service_cmd}" "${cmd}" "${action}" + run "${systemctl_cmd}" "${action}" "${cmd}" return $? - elif [ ! -z "${systemctl_cmd}" ] + elif [ ! -z "${service_cmd}" ] then - run "${systemctl_cmd}" "${action}" "${cmd}" + run "${service_cmd}" "${cmd}" "${action}" return $? fi return 1 @@ -142,9 +146,22 @@ run_failed() { printf >&2 "${TPUT_BGRED}${TPUT_WHITE}${TPUT_BOLD} FAILED ${TPUT_RESET} ${*} \n\n" } +ESCAPED_PRINT_METHOD= +printf "%q " test >/dev/null 2>&1 +[ $? -eq 0 ] && ESCAPED_PRINT_METHOD="printfq" +escaped_print() { + if [ "${ESCAPED_PRINT_METHOD}" = "printfq" ] + then + printf "%q " "${@}" + else + printf "%s" "${*}" + fi + return 0 +} + run_logfile="/dev/null" run() { - local user="${USER}" dir="$(basename "${PWD}")" info info_console + local user="${USER--}" dir="${PWD}" info info_console if [ "${UID}" = "0" ] then @@ -156,11 +173,11 @@ run() { fi printf >> "${run_logfile}" "${info}" - printf >> "${run_logfile}" "%q " "${@}" + escaped_print >> "${run_logfile}" "${@}" printf >> "${run_logfile}" " ... " printf >&2 "${info_console}${TPUT_BOLD}${TPUT_YELLOW}" - printf >&2 "%q " "${@}" + escaped_print >&2 "${@}" printf >&2 "${TPUT_RESET}\n" "${@}" @@ -178,10 +195,53 @@ run() { return ${ret} } +getent_cmd="$(which_cmd getent)" +portable_check_user_exists() { + local username="${1}" found= + + if [ ! -z "${getent_cmd}" ] + then + "${getent_cmd}" passwd "${username}" >/dev/null 2>&1 + return $? + fi + + found="$(cut -d ':' -f 1 /dev/null 2>&1 + return $? + fi + + found="$(cut -d ':' -f 1 /dev/null 2>&1 + portable_check_user_exists "${username}" [ $? -eq 0 ] && echo >&2 "User '${username}' already exists." && return 0 echo >&2 "Adding ${username} user account ..." @@ -214,7 +274,7 @@ portable_add_user() { portable_add_group() { local groupname="${1}" - getent group "${groupname}" > /dev/null 2>&1 + portable_check_group_exists "${groupname}" [ $? -eq 0 ] && echo >&2 "Group '${groupname}' already exists." && return 0 echo >&2 "Adding ${groupname} user group ..." @@ -244,12 +304,11 @@ portable_add_group() { portable_add_user_to_group() { local groupname="${1}" username="${2}" - getent group "${groupname}" > /dev/null 2>&1 + portable_check_group_exists "${groupname}" [ $? -ne 0 ] && echo >&2 "Group '${groupname}' does not exist." && return 1 # find the user is already in the group - local users=$(getent group "${groupname}" | cut -d ':' -f 4) - if [[ ",${users}," =~ ,${username}, ]] + if portable_check_user_in_group "${username}" "${groupname}" then # username is already there echo >&2 "User '${username}' is already in group '${groupname}'." @@ -342,3 +401,258 @@ issystemd() { # else, it is not systemd return 1 } + +install_non_systemd_init() { + [ "${UID}" != 0 ] && return 1 + + local key="unknown" + if [ -f /etc/os-release ] + then + source /etc/os-release || return 1 + key="${ID}-${VERSION_ID}" + + elif [ -f /etc/redhat-release ] + then + key=$(&2 "Installing OpenRC init file..." + run cp system/netdata-openrc /etc/init.d/netdata && \ + run chmod 755 /etc/init.d/netdata && \ + run rc-update add netdata default && \ + return 0 + + elif [ "${key}" = "debian-7" \ + -o "${key}" = "ubuntu-12.04" \ + -o "${key}" = "ubuntu-14.04" \ + ] + then + echo >&2 "Installing LSB init file..." + run cp system/netdata-lsb /etc/init.d/netdata && \ + run chmod 755 /etc/init.d/netdata && \ + run update-rc.d netdata defaults && \ + run update-rc.d netdata enable && \ + return 0 + elif [[ "${key}" =~ ^(amzn-201[567]|CentOS release 6|Red Hat Enterprise Linux Server release 6).* ]] + then + echo >&2 "Installing init.d file..." + run cp system/netdata-init-d /etc/init.d/netdata && \ + run chmod 755 /etc/init.d/netdata && \ + run chkconfig netdata on && \ + return 0 + else + echo >&2 "I don't know what init file to install on system '${key}'. Open a github issue to help us fix it." + return 1 + fi + elif [ -f /etc/init.d/netdata ] + then + echo >&2 "file '/etc/init.d/netdata' already exists." + return 0 + else + echo >&2 "I don't know what init file to install on system '${key}'. Open a github issue to help us fix it." + fi + + return 1 +} + +NETDATA_START_CMD="netdata" +NETDATA_STOP_CMD="killall netdata" + +install_netdata_service() { + if [ "${UID}" -eq 0 ] + then + if issystemd + then + # systemd is running on this system + NETDATA_START_CMD="systemctl start netdata" + NETDATA_STOP_CMD="systemctl stop netdata" + + if [ ! -f /etc/systemd/system/netdata.service ] + then + echo >&2 "Installing systemd service..." + run cp system/netdata.service /etc/systemd/system/netdata.service && \ + run systemctl daemon-reload && \ + run systemctl enable netdata && \ + return 0 + else + echo >&2 "file '/etc/systemd/system/netdata.service' already exists." + return 0 + fi + else + install_non_systemd_init + local ret=$? + + if [ ${ret} -eq 0 ] + then + NETDATA_START_CMD="service netdata start" + NETDATA_STOP_CMD="service netdata stop" + fi + + return ${ret} + fi + fi + + return 1 +} + + +# ----------------------------------------------------------------------------- +# stop netdata + +pidisnetdata() { + if [ -d /proc/self ] + then + [ -z "$1" -o ! -f "/proc/$1/stat" ] && return 1 + [ "$(cat "/proc/$1/stat" | cut -d '(' -f 2 | cut -d ')' -f 1)" = "netdata" ] && return 0 + return 1 + fi + return 0 +} + +stop_netdata_on_pid() { + local pid="${1}" ret=0 count=0 + + pidisnetdata ${pid} || return 0 + + printf >&2 "Stopping netdata on pid ${pid} ..." + while [ ! -z "$pid" -a ${ret} -eq 0 ] + do + if [ ${count} -gt 45 ] + then + echo >&2 "Cannot stop the running netdata on pid ${pid}." + return 1 + fi + + count=$(( count + 1 )) + + run kill ${pid} 2>/dev/null + ret=$? + + test ${ret} -eq 0 && printf >&2 "." && sleep 2 + done + + echo >&2 + if [ ${ret} -eq 0 ] + then + echo >&2 "SORRY! CANNOT STOP netdata ON PID ${pid} !" + return 1 + fi + + echo >&2 "netdata on pid ${pid} stopped." + return 0 +} + +stop_all_netdata() { + local p myns ns + + myns="$(readlink /proc/self/ns/pid 2>/dev/null)" + + # echo >&2 "Stopping a (possibly) running netdata (namespace '${myns}')..." + + for p in \ + $(cat /var/run/netdata.pid 2>/dev/null) \ + $(cat /var/run/netdata/netdata.pid 2>/dev/null) \ + $(pidof netdata 2>/dev/null) + do + ns="$(readlink /proc/${p}/ns/pid 2>/dev/null)" + + if [ -z "${myns}" -o -z "${ns}" -o "${myns}" = "${ns}" ] + then + stop_netdata_on_pid ${p} + fi + done +} + +# ----------------------------------------------------------------------------- +# restart netdata + +restart_netdata() { + local netdata="${1}" + shift + + local started=0 + + progress "Start netdata" + + if [ "${UID}" -eq 0 ] + then + service netdata stop + stop_all_netdata + service netdata restart && started=1 + + if [ ${started} -eq 0 ] + then + service netdata start && started=1 + fi + fi + + if [ ${started} -eq 0 ] + then + # still not started... + + run stop_all_netdata + run "${netdata}" "${@}" + return $? + fi + + return 0 +} + +# ----------------------------------------------------------------------------- +# install netdata logrotate + +install_netdata_logrotate() { + if [ ${UID} -eq 0 ] + then + if [ -d /etc/logrotate.d ] + then + if [ ! -f /etc/logrotate.d/netdata ] + then + run cp system/netdata.logrotate /etc/logrotate.d/netdata + fi + + if [ -f /etc/logrotate.d/netdata ] + then + run chmod 644 /etc/logrotate.d/netdata + fi + + return 0 + fi + fi + + return 1 +} + +# ----------------------------------------------------------------------------- +# add netdata user and group + +NETDATA_ADDED_TO_DOCKER=0 +NETDATA_ADDED_TO_NGINX=0 +NETDATA_ADDED_TO_VARNISH=0 +NETDATA_ADDED_TO_HAPROXY=0 +NETDATA_ADDED_TO_ADM=0 +NETDATA_ADDED_TO_NSD=0 +NETDATA_ADDED_TO_PROXY=0 +NETDATA_ADDED_TO_SQUID=0 +add_netdata_user_and_group() { + if [ ${UID} -eq 0 ] + then + portable_add_group netdata || return 1 + portable_add_user netdata || return 1 + portable_add_user_to_group docker netdata && NETDATA_ADDED_TO_DOCKER=1 + portable_add_user_to_group nginx netdata && NETDATA_ADDED_TO_NGINX=1 + portable_add_user_to_group varnish netdata && NETDATA_ADDED_TO_VARNISH=1 + portable_add_user_to_group haproxy netdata && NETDATA_ADDED_TO_HAPROXY=1 + portable_add_user_to_group adm netdata && NETDATA_ADDED_TO_ADM=1 + portable_add_user_to_group nsd netdata && NETDATA_ADDED_TO_NSD=1 + portable_add_user_to_group proxy netdata && NETDATA_ADDED_TO_PROXY=1 + portable_add_user_to_group squid netdata && NETDATA_ADDED_TO_SQUID=1 + return 0 + fi + + return 1 +} diff --git a/kickstart-static64.sh b/kickstart-static64.sh new file mode 100755 index 000000000..98c224f47 --- /dev/null +++ b/kickstart-static64.sh @@ -0,0 +1,232 @@ +#!/usr/bin/env sh + +umask 022 + +# --------------------------------------------------------------------------------------------------------------------- +# library functions copied from installer/functions.sh + +which_cmd() { + which "${1}" 2>/dev/null || \ + command -v "${1}" 2>/dev/null +} + +check_cmd() { + which_cmd "${1}" >/dev/null 2>&1 && return 0 + return 1 +} + +setup_terminal() { + TPUT_RESET="" + TPUT_BLACK="" + TPUT_RED="" + TPUT_GREEN="" + TPUT_YELLOW="" + TPUT_BLUE="" + TPUT_PURPLE="" + TPUT_CYAN="" + TPUT_WHITE="" + TPUT_BGBLACK="" + TPUT_BGRED="" + TPUT_BGGREEN="" + TPUT_BGYELLOW="" + TPUT_BGBLUE="" + TPUT_BGPURPLE="" + TPUT_BGCYAN="" + TPUT_BGWHITE="" + TPUT_BOLD="" + TPUT_DIM="" + TPUT_UNDERLINED="" + TPUT_BLINK="" + TPUT_INVERTED="" + TPUT_STANDOUT="" + TPUT_BELL="" + TPUT_CLEAR="" + + # Is stderr on the terminal? If not, then fail + test -t 2 || return 1 + + if check_cmd tput + then + if [ $(( $(tput colors 2>/dev/null) )) -ge 8 ] + then + # Enable colors + TPUT_RESET="$(tput sgr 0)" + TPUT_BLACK="$(tput setaf 0)" + TPUT_RED="$(tput setaf 1)" + TPUT_GREEN="$(tput setaf 2)" + TPUT_YELLOW="$(tput setaf 3)" + TPUT_BLUE="$(tput setaf 4)" + TPUT_PURPLE="$(tput setaf 5)" + TPUT_CYAN="$(tput setaf 6)" + TPUT_WHITE="$(tput setaf 7)" + TPUT_BGBLACK="$(tput setab 0)" + TPUT_BGRED="$(tput setab 1)" + TPUT_BGGREEN="$(tput setab 2)" + TPUT_BGYELLOW="$(tput setab 3)" + TPUT_BGBLUE="$(tput setab 4)" + TPUT_BGPURPLE="$(tput setab 5)" + TPUT_BGCYAN="$(tput setab 6)" + TPUT_BGWHITE="$(tput setab 7)" + TPUT_BOLD="$(tput bold)" + TPUT_DIM="$(tput dim)" + TPUT_UNDERLINED="$(tput smul)" + TPUT_BLINK="$(tput blink)" + TPUT_INVERTED="$(tput rev)" + TPUT_STANDOUT="$(tput smso)" + TPUT_BELL="$(tput bel)" + TPUT_CLEAR="$(tput clear)" + fi + fi + + return 0 +} +setup_terminal || echo >/dev/null + +progress() { + echo >&2 " --- ${TPUT_DIM}${TPUT_BOLD}${*}${TPUT_RESET} --- " +} + +run_ok() { + printf >&2 "${TPUT_BGGREEN}${TPUT_WHITE}${TPUT_BOLD} OK ${TPUT_RESET} ${*} \n\n" +} + +run_failed() { + printf >&2 "${TPUT_BGRED}${TPUT_WHITE}${TPUT_BOLD} FAILED ${TPUT_RESET} ${*} \n\n" +} + +ESCAPED_PRINT_METHOD= +printf "%q " test >/dev/null 2>&1 +[ $? -eq 0 ] && ESCAPED_PRINT_METHOD="printfq" +escaped_print() { + if [ "${ESCAPED_PRINT_METHOD}" = "printfq" ] + then + printf "%q " "${@}" + else + printf "%s" "${*}" + fi + return 0 +} + +run_logfile="/dev/null" +run() { + local user="${USER--}" dir="${PWD}" info info_console + + if [ "${UID}" = "0" ] + then + info="[root ${dir}]# " + info_console="[${TPUT_DIM}${dir}${TPUT_RESET}]# " + else + info="[${user} ${dir}]$ " + info_console="[${TPUT_DIM}${dir}${TPUT_RESET}]$ " + fi + + printf >> "${run_logfile}" "${info}" + escaped_print >> "${run_logfile}" "${@}" + printf >> "${run_logfile}" " ... " + + printf >&2 "${info_console}${TPUT_BOLD}${TPUT_YELLOW}" + escaped_print >&2 "${@}" + printf >&2 "${TPUT_RESET}\n" + + "${@}" + + local ret=$? + if [ ${ret} -ne 0 ] + then + run_failed + printf >> "${run_logfile}" "FAILED with exit code ${ret}\n" + else + run_ok + printf >> "${run_logfile}" "OK\n" + fi + + return ${ret} +} + + +# --------------------------------------------------------------------------------------------------------------------- + +fatal() { + printf >&2 "${TPUT_BGRED}${TPUT_WHITE}${TPUT_BOLD} ABORTED ${TPUT_RESET} ${*} \n\n" + exit 1 +} + +# --------------------------------------------------------------------------------------------------------------------- + +if [ "$(uname -m)" != "x86_64" ] + then + fatal "Static binary versions of netdata are available only for 64bit Intel/AMD CPUs (x86_64), but yours is: $(uname -m)." +fi + +if [ "$(uname -s)" != "Linux" ] + then + fatal "Static binary versions of netdata are available only for Linux, but this system is $(uname -s)" +fi + +curl="$(which_cmd curl)" +wget="$(which_cmd wget)" + +# --------------------------------------------------------------------------------------------------------------------- + +progress "Checking the latest version of static build..." + +BASE='https://raw.githubusercontent.com/firehol/binary-packages/master' + +LATEST= +if [ ! -z "${curl}" -a -x "${curl}" ] +then + LATEST="$(run ${curl} "${BASE}/netdata-latest.gz.run")" +elif [ ! -z "${wget}" -a -x "${wget}" ] +then + LATEST="$(run ${wget} -O - "${BASE}/netdata-latest.gz.run")" +else + fatal "curl or wget are needed for this script to work." +fi + +if [ -z "${LATEST}" ] + then + fatal "Cannot find the latest static binary version of netdata." +fi + +# --------------------------------------------------------------------------------------------------------------------- + +progress "Downloading static netdata binary: ${LATEST}" + +ret=1 +if [ ! -z "${curl}" -a -x "${curl}" ] +then + run ${curl} "${BASE}/${LATEST}" >"/tmp/${LATEST}" + ret=$? +elif [ ! -z "${wget}" -a -x "${wget}" ] +then + run ${wget} -O "/tmp/${LATEST}" "${BASE}/${LATEST}" + ret=$? +else + fatal "curl or wget are needed for this script to work." +fi + +if [ ${ret} -ne 0 -o ! -s "/tmp/${LATEST}" ] + then + fatal "Failed to download the latest static binary version of netdata." +fi + +# --------------------------------------------------------------------------------------------------------------------- + +opts= +if [ "${1}" = "--dont-wait" -o "${1}" = "--non-interactive" ] +then + opts="--accept" +fi + +progress "Installing netdata" + +sudo= +[ "${UID}" != "0" ] && sudo="sudo" +run ${sudo} sh "/tmp/${LATEST}" ${opts} + +if [ $? -eq 0 ] + then + rm "/tmp/${LATEST}" +else + echo >&2 "NOTE: did not remove: /tmp/${LATEST}" +fi diff --git a/kickstart.sh b/kickstart.sh new file mode 100755 index 000000000..cabe0146c --- /dev/null +++ b/kickstart.sh @@ -0,0 +1,374 @@ +#!/usr/bin/env sh +# +# Run me with: +# +# bash <(curl -Ss https://my-netdata.io/kickstart.sh) +# +# or (to install all netdata dependencies): +# +# bash <(curl -Ss https://my-netdata.io/kickstart.sh) all +# +# Other options: +# --src-dir PATH keep netdata.git at PATH/netdata.git +# --dont-wait do not prompt for user input +# --non-interactive do not prompt for user input +# --no-updates do not install script for daily updates +# +# This script will: +# +# 1. install all netdata compilation dependencies +# using the package manager of the system +# +# 2. download netdata source code in /usr/src/netdata.git +# +# 3. install netdata + +umask 022 + +[ -z "${UID}" ] && UID="$(id -u)" + +# --------------------------------------------------------------------------------------------------------------------- +# library functions copied from installer/functions.sh + +which_cmd() { + which "${1}" 2>/dev/null || \ + command -v "${1}" 2>/dev/null +} + +check_cmd() { + which_cmd "${1}" >/dev/null 2>&1 && return 0 + return 1 +} + +setup_terminal() { + TPUT_RESET="" + TPUT_BLACK="" + TPUT_RED="" + TPUT_GREEN="" + TPUT_YELLOW="" + TPUT_BLUE="" + TPUT_PURPLE="" + TPUT_CYAN="" + TPUT_WHITE="" + TPUT_BGBLACK="" + TPUT_BGRED="" + TPUT_BGGREEN="" + TPUT_BGYELLOW="" + TPUT_BGBLUE="" + TPUT_BGPURPLE="" + TPUT_BGCYAN="" + TPUT_BGWHITE="" + TPUT_BOLD="" + TPUT_DIM="" + TPUT_UNDERLINED="" + TPUT_BLINK="" + TPUT_INVERTED="" + TPUT_STANDOUT="" + TPUT_BELL="" + TPUT_CLEAR="" + + # Is stderr on the terminal? If not, then fail + test -t 2 || return 1 + + if check_cmd tput + then + if [ $(( $(tput colors 2>/dev/null) )) -ge 8 ] + then + # Enable colors + TPUT_RESET="$(tput sgr 0)" + TPUT_BLACK="$(tput setaf 0)" + TPUT_RED="$(tput setaf 1)" + TPUT_GREEN="$(tput setaf 2)" + TPUT_YELLOW="$(tput setaf 3)" + TPUT_BLUE="$(tput setaf 4)" + TPUT_PURPLE="$(tput setaf 5)" + TPUT_CYAN="$(tput setaf 6)" + TPUT_WHITE="$(tput setaf 7)" + TPUT_BGBLACK="$(tput setab 0)" + TPUT_BGRED="$(tput setab 1)" + TPUT_BGGREEN="$(tput setab 2)" + TPUT_BGYELLOW="$(tput setab 3)" + TPUT_BGBLUE="$(tput setab 4)" + TPUT_BGPURPLE="$(tput setab 5)" + TPUT_BGCYAN="$(tput setab 6)" + TPUT_BGWHITE="$(tput setab 7)" + TPUT_BOLD="$(tput bold)" + TPUT_DIM="$(tput dim)" + TPUT_UNDERLINED="$(tput smul)" + TPUT_BLINK="$(tput blink)" + TPUT_INVERTED="$(tput rev)" + TPUT_STANDOUT="$(tput smso)" + TPUT_BELL="$(tput bel)" + TPUT_CLEAR="$(tput clear)" + fi + fi + + return 0 +} +setup_terminal || echo >/dev/null + +progress() { + echo >&2 " --- ${TPUT_DIM}${TPUT_BOLD}${*}${TPUT_RESET} --- " +} + +run_ok() { + printf >&2 "${TPUT_BGGREEN}${TPUT_WHITE}${TPUT_BOLD} OK ${TPUT_RESET} ${*} \n\n" +} + +run_failed() { + printf >&2 "${TPUT_BGRED}${TPUT_WHITE}${TPUT_BOLD} FAILED ${TPUT_RESET} ${*} \n\n" +} + +ESCAPED_PRINT_METHOD= +printf "%q " test >/dev/null 2>&1 +[ $? -eq 0 ] && ESCAPED_PRINT_METHOD="printfq" +escaped_print() { + if [ "${ESCAPED_PRINT_METHOD}" = "printfq" ] + then + printf "%q " "${@}" + else + printf "%s" "${*}" + fi + return 0 +} + +run_logfile="/dev/null" +run() { + local user="${USER--}" dir="${PWD}" info info_console + + if [ "${UID}" = "0" ] + then + info="[root ${dir}]# " + info_console="[${TPUT_DIM}${dir}${TPUT_RESET}]# " + else + info="[${user} ${dir}]$ " + info_console="[${TPUT_DIM}${dir}${TPUT_RESET}]$ " + fi + + printf >> "${run_logfile}" "${info}" + escaped_print >> "${run_logfile}" "${@}" + printf >> "${run_logfile}" " ... " + + printf >&2 "${info_console}${TPUT_BOLD}${TPUT_YELLOW}" + escaped_print >&2 "${@}" + printf >&2 "${TPUT_RESET}\n" + + "${@}" + + local ret=$? + if [ ${ret} -ne 0 ] + then + run_failed + printf >> "${run_logfile}" "FAILED with exit code ${ret}\n" + else + run_ok + printf >> "${run_logfile}" "OK\n" + fi + + return ${ret} +} + + +# --------------------------------------------------------------------------------------------------------------------- +# collect system information + +fatal() { + printf >&2 "${TPUT_BGRED}${TPUT_WHITE}${TPUT_BOLD} ABORTED ${TPUT_RESET} ${*} \n\n" + exit 1 +} + +export PATH="${PATH}:/usr/local/bin:/usr/local/sbin" + +curl="$(which_cmd curl)" +wget="$(which_cmd wget)" +bash="$(which_cmd bash)" + +if [ -z "${BASH_VERSION}" ] +then + # we don't run under bash + if [ ! -z "${bash}" -a -x "${bash}" ] + then + BASH_MAJOR_VERSION=$(${bash} -c 'echo "${BASH_VERSINFO[0]}"') + fi +else + # we run under bash + BASH_MAJOR_VERSION="${BASH_VERSINFO[0]}" +fi + +HAS_BASH4=1 +if [ -z "${BASH_MAJOR_VERSION}" ] +then + echo >&2 "No BASH is available on this system" + HAS_BASH4=0 +elif [ $((BASH_MAJOR_VERSION)) -lt 4 ] +then + echo >&2 "No BASH v4+ is available on this system (installed bash is v${BASH_MAJOR_VERSION}" + HAS_BASH4=0 +fi + +SYSTEM="$(uname -s)" +OS="$(uname -o)" +MACHINE="$(uname -m)" + +cat <&2 "netdata source will be installed at ${SOURCE_DST}/netdata.git" + shift 2 + elif [ "${1}" = "--no-updates" ] + then + # echo >&2 "netdata will not auto-update" + NETDATA_UPDATES= + shift 1 + else + break + fi +done + +if [ "${INTERACTIVE}" = "0" ] +then + PACKAGES_INSTALLER_OPTIONS="--dont-wait --non-interactive ${PACKAGES_INSTALLER_OPTIONS}" + NETDATA_INSTALLER_OPTIONS="--dont-wait" +fi + +# echo "PACKAGES_INSTALLER_OPTIONS=${PACKAGES_INSTALLER_OPTIONS}" +# echo "NETDATA_INSTALLER_OPTIONS=${NETDATA_INSTALLER_OPTIONS} ${*}" + +if [ "${OS}" = "GNU/Linux" -o "${SYSTEM}" = "Linux" ] +then + if [ "${HAS_BASH4}" = "1" ] + then + tmp="$(mktemp /tmp/netdata-kickstart-XXXXXX)" + url="https://raw.githubusercontent.com/firehol/netdata-demo-site/master/install-required-packages.sh" + + progress "Downloading script to detect required packages..." + if [ ! -z "${curl}" ] + then + run ${curl} "${url}" >"${tmp}" || fatal "Cannot download ${url}" + elif [ ! -z "${wget}" ] + then + run "${wget}" -O - "${url}" >"${tmp}" || fatal "Cannot download ${url}" + else + rm "${tmp}" + fatal "I need curl or wget to proceed, but neither is available on this system." + fi + + ask=0 + if [ -s "${tmp}" ] + then + progress "Running downloaded script to detect required packages..." + run ${sudo} "${bash}" "${tmp}" ${PACKAGES_INSTALLER_OPTIONS} || ask=1 + rm "${tmp}" + else + rm "${tmp}" + fatal "Downloaded script is empty..." + fi + + if [ "${ask}" = "1" ] + then + echo >&2 "It failed to install all the required packages, but I can try to install netdata." + read -p "Press ENTER to continue to netdata installation > " + progress "OK, let's give it a try..." + fi + else + echo >&2 "WARNING" + echo >&2 "Cannot detect the packages to be installed in this system, without BASH v4+." + echo >&2 "We can only attempt to install netdata..." + echo >&2 + fi +else + echo >&2 "WARNING" + echo >&2 "Cannot detect the packages to be installed on a ${SYSTEM} - ${OS} system." + echo >&2 "We can only attempt to install netdata..." + echo >&2 +fi + + +# --------------------------------------------------------------------------------------------------------------------- +# download netdata source + +# this has to checked after we have installed the required packages +git="$(which_cmd git)" + +NETDATA_SOURCE_DIR= +if [ ! -z "${git}" -a -x "${git}" ] +then + [ ! -d "${SOURCE_DST}" ] && run ${sudo} mkdir -p "${SOURCE_DST}" + + if [ ! -d "${SOURCE_DST}/netdata.git" ] + then + progress "Downloading netdata source code..." + run ${sudo} ${git} clone https://github.com/firehol/netdata.git "${SOURCE_DST}/netdata.git" || fatal "Cannot download netdata source" + cd "${SOURCE_DST}/netdata.git" || fatal "Cannot cd to netdata source tree" + else + progress "Updating netdata source code..." + cd "${SOURCE_DST}/netdata.git" || fatal "Cannot cd to netdata source tree" + run ${sudo} ${git} fetch --all || fatal "Cannot fetch netdata source updates" + run ${sudo} ${git} reset --hard origin/master || fatal "Cannot update netdata source tree" + fi + NETDATA_SOURCE_DIR="${SOURCE_DST}/netdata.git" +else + fatal "Cannot find the command 'git' to download the netdata source code." +fi + + +# --------------------------------------------------------------------------------------------------------------------- +# install netdata from source + +if [ ! -z "${NETDATA_SOURCE_DIR}" -a -d "${NETDATA_SOURCE_DIR}" ] +then + cd "${NETDATA_SOURCE_DIR}" || fatal "Cannot cd to netdata source tree" + + install=0 + if [ -x netdata-updater.sh ] + then + # attempt to run the updater, to respect any compilation settings already in place + progress "Re-installing netdata..." + run ${sudo} ./netdata-updater.sh -f || install=1 + else + install=1 + fi + + if [ "${install}" = "1" ] + then + if [ -x netdata-installer.sh ] + then + progress "Installing netdata..." + run ${sudo} ./netdata-installer.sh ${NETDATA_UPDATES} ${NETDATA_INSTALLER_OPTIONS} "${@}" || \ + fatal "netdata-installer.sh exited with error" + else + fatal "Cannot install netdata from source (the source directory does not include netdata-installer.sh)." + fi + fi +else + fatal "Cannot install netdata from source, on this system (cannot download the source code)." +fi diff --git a/m4/ax_c___atomic.m4 b/m4/ax_c___atomic.m4 index 131929ae1..dd5ee3d1c 100644 --- a/m4/ax_c___atomic.m4 +++ b/m4/ax_c___atomic.m4 @@ -10,13 +10,19 @@ AC_DEFUN([AC_C___ATOMIC], main (int argc, char **argv) { volatile unsigned long ul1 = 1, ul2 = 0, ul3 = 2; + __atomic_load_n(&ul1, __ATOMIC_SEQ_CST); __atomic_compare_exchange(&ul1, &ul2, &ul3, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); __atomic_fetch_add(&ul1, 1, __ATOMIC_SEQ_CST); __atomic_fetch_sub(&ul3, 1, __ATOMIC_SEQ_CST); + __atomic_or_fetch(&ul1, ul2, __ATOMIC_SEQ_CST); + __atomic_and_fetch(&ul1, ul2, __ATOMIC_SEQ_CST); volatile unsigned long long ull1 = 1, ull2 = 0, ull3 = 2; + __atomic_load_n(&ull1, __ATOMIC_SEQ_CST); __atomic_compare_exchange(&ull1, &ull2, &ull3, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); __atomic_fetch_add(&ull1, 1, __ATOMIC_SEQ_CST); __atomic_fetch_sub(&ull3, 1, __ATOMIC_SEQ_CST); + __atomic_or_fetch(&ull1, ull2, __ATOMIC_SEQ_CST); + __atomic_and_fetch(&ull1, ull2, __ATOMIC_SEQ_CST); return 0; } ]])], diff --git a/makeself/build-x86_64-static.sh b/makeself/build-x86_64-static.sh new file mode 100755 index 000000000..0516beae2 --- /dev/null +++ b/makeself/build-x86_64-static.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env sh + +set -e + +DOCKER_CONTAINER_NAME="netdata-package-x86_64-static" + +if ! sudo docker inspect "${DOCKER_CONTAINER_NAME}" >/dev/null 2>&1 +then + # To run interactively: + # sudo docker run -it netdata-package-x86_64-static /bin/sh + # (add -v host-dir:guest-dir:rw arguments to mount volumes) + # + # To remove images in order to re-create: + # sudo docker rm -v $(sudo docker ps -a -q -f status=exited) + # sudo docker rmi netdata-package-x86_64-static + # + # This command maps the current directory to + # /usr/src/netdata.git + # inside the container and runs the script setup-x86_64-static.sh + # (also inside the container) + # + sudo docker run -v $(pwd):/usr/src/netdata.git:rw alpine:3.5 \ + /bin/sh /usr/src/netdata.git/makeself/setup-x86_64-static.sh + + # save the changes made permanently + id=$(sudo docker ps -l -q) + sudo docker commit ${id} "${DOCKER_CONTAINER_NAME}" +fi + +# Run the build script inside the container +sudo docker run -a stdin -a stdout -a stderr -i -t -v \ + $(pwd):/usr/src/netdata.git:rw \ + "${DOCKER_CONTAINER_NAME}" \ + /bin/sh /usr/src/netdata.git/makeself/build.sh + +if [ "${USER}" ] + then + sudo chown -R "${USER}" . +fi diff --git a/makeself/build.sh b/makeself/build.sh new file mode 100755 index 000000000..7896425d7 --- /dev/null +++ b/makeself/build.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env sh + +# First run setup-x86_64-static.sh under alpine linux to install +# the required packages. build-x86_64-static.sh will do this for you +# using docker. + +cd $(dirname "$0") || exit 1 + +# if we don't run inside the netdata repo +# download it and run from it +if [ ! -f ../netdata-installer.sh ] +then + git clone https://github.com/firehol/netdata.git netdata.git || exit 1 + cd netdata.git/makeself || exit 1 + ./build.sh "$@" + exit $? +fi + +cat >&2 < " + +if [ ! -d tmp ] + then + mkdir tmp || exit 1 +fi + +./run-all-jobs.sh "$@" +exit $? diff --git a/makeself/functions.sh b/makeself/functions.sh new file mode 100755 index 000000000..48835f0f5 --- /dev/null +++ b/makeself/functions.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# ----------------------------------------------------------------------------- + +# allow running the jobs by hand +[ -z "${NETDATA_INSTALL_PATH}" ] && export NETDATA_INSTALL_PATH="${1-/opt/netdata}" +[ -z "${NETDATA_MAKESELF_PATH}" ] && export NETDATA_MAKESELF_PATH="$(dirname "${0}")/.." +[ "${NETDATA_MAKESELF_PATH:0:1}" != "/" ] && export NETDATA_MAKESELF_PATH="$(pwd)/${NETDATA_MAKESELF_PATH}" +[ -z "${NETDATA_SOURCE_PATH}" ] && export NETDATA_SOURCE_PATH="${NETDATA_MAKESELF_PATH}/.." +[ -z "${PROCESSORS}" ] && export PROCESSORS=$(cat /proc/cpuinfo 2>/dev/null | grep ^processor | wc -l) +[ -z "${PROCESSORS}" -o $((PROCESSORS)) -lt 1 ] && export PROCESSORS=1 +export NULL= + +# make sure the path does not end with / +if [ "${NETDATA_INSTALL_PATH:$(( ${#NETDATA_INSTALL_PATH} - 1)):1}" = "/" ] + then + export NETDATA_INSTALL_PATH="${NETDATA_INSTALL_PATH:0:$(( ${#NETDATA_INSTALL_PATH} - 1))}" +fi + +# find the parent directory +export NETDATA_INSTALL_PARENT="$(dirname "${NETDATA_INSTALL_PATH}")" + + +# debug +echo "ME=${0}" +echo "NETDATA_INSTALL_PARENT=${NETDATA_INSTALL_PARENT}" +echo "NETDATA_INSTALL_PATH=${NETDATA_INSTALL_PATH}" +echo "NETDATA_MAKESELF_PATH=${NETDATA_MAKESELF_PATH}" +echo "NETDATA_SOURCE_PATH=${NETDATA_SOURCE_PATH}" +echo "PROCESSORS=${PROCESSORS}" + +# bash strict mode +set -euo pipefail + +# ----------------------------------------------------------------------------- + +fetch() { + local dir="${1}" url="${2}" + local tar="${dir}.tar.gz" + + if [ ! -f "${NETDATA_MAKESELF_PATH}/tmp/${tar}" ] + then + run wget -O "${NETDATA_MAKESELF_PATH}/tmp/${tar}" "${url}" + fi + + if [ ! -d "${NETDATA_MAKESELF_PATH}/tmp/${dir}" ] + then + cd "${NETDATA_MAKESELF_PATH}/tmp" + run tar -zxvpf "${tar}" + cd - + fi + + run cd "${NETDATA_MAKESELF_PATH}/tmp/${dir}" +} + +# ----------------------------------------------------------------------------- + +# load the functions of the netdata-installer.sh +. "${NETDATA_SOURCE_PATH}/installer/functions.sh" diff --git a/makeself/install-or-update.sh b/makeself/install-or-update.sh new file mode 100755 index 000000000..da63c64b6 --- /dev/null +++ b/makeself/install-or-update.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash + +. $(dirname "${0}")/functions.sh + +export LC_ALL=C +umask 002 + +# Be nice on production environments +renice 19 $$ >/dev/null 2>/dev/null + + +# ----------------------------------------------------------------------------- +progress "Checking new configuration files" + +declare -A configs_signatures=() +. system/configs.signatures + +if [ ! -d etc/netdata ] + then + run mkdir -p etc/netdata +fi + +md5sum="$(which md5sum 2>/dev/null || command -v md5sum 2>/dev/null || command -v md5 2>/dev/null)" +for x in $(find etc.new -type f) +do + # find it relative filename + f="${x/etc.new\/netdata\//}" + t="${x/etc.new\//etc\/}" + d=$(dirname "${t}") + + #echo >&2 "x: ${x}" + #echo >&2 "t: ${t}" + #echo >&2 "d: ${d}" + + if [ ! -d "${d}" ] + then + run mkdir -p "${d}" + fi + + if [ ! -f "${t}" ] + then + run cp "${x}" "${t}" + continue + fi + + if [ ! -z "${md5sum}" ] + then + # find the checksum of the existing file + md5="$(cat "${t}" | ${md5sum} | cut -d ' ' -f 1)" + #echo >&2 "md5: ${md5}" + + # check if it matches + if [ "${configs_signatures[${md5}]}" = "${f}" ] + then + run cp "${x}" "${t}" + fi + fi + + if ! [[ "${x}" =~ .*\.orig ]] + then + run mv "${x}" "${t}.orig" + fi +done + +run rm -rf etc.new + + +# ----------------------------------------------------------------------------- +progress "Add user netdata to required user groups" + +NETDATA_USER="root" +NETDATA_GROUP="root" +add_netdata_user_and_group +if [ $? -eq 0 ] + then + NETDATA_USER="netdata" + NETDATA_GROUP="netdata" +else + run_failed "Failed to add netdata user and group" +fi + + +# ----------------------------------------------------------------------------- +progress "Install logrotate configuration for netdata" + +install_netdata_logrotate || run_failed "Cannot install logrotate file for netdata." + + +# ----------------------------------------------------------------------------- +progress "Install netdata at system init" + +install_netdata_service || run_failed "Cannot install netdata init service." + + +# ----------------------------------------------------------------------------- +progress "creating quick links" + +dir_should_be_link() { + local p="${1}" t="${2}" d="${3}" old + + old="${PWD}" + cd "${p}" || return 0 + + if [ -e "${d}" ] + then + if [ -h "${d}" ] + then + run rm "${d}" + else + run mv -f "${d}" "${d}.old.$$" + fi + fi + + run ln -s "${t}" "${d}" + cd "${old}" +} + +dir_should_be_link . bin sbin +dir_should_be_link usr ../bin bin +dir_should_be_link usr ../bin sbin +dir_should_be_link usr . local + +dir_should_be_link . etc/netdata netdata-configs +dir_should_be_link . usr/share/netdata/web netdata-web-files +dir_should_be_link . usr/libexec/netdata netdata-plugins +dir_should_be_link . var/lib/netdata netdata-dbs +dir_should_be_link . var/cache/netdata netdata-metrics +dir_should_be_link . var/log/netdata netdata-logs + + +# ----------------------------------------------------------------------------- +progress "fix permissions" + +run chmod g+rx,o+rx /opt +run chown -R ${NETDATA_USER}:${NETDATA_GROUP} /opt/netdata + + +# ----------------------------------------------------------------------------- +progress "fix plugin permissions" + +for x in apps.plugin freeipmi.plugin +do + f="usr/libexec/netdata/plugins.d/${x}" + + if [ -f "${f}" ] + then + run chown root:${NETDATA_GROUP} "${f}" + run chmod 4750 "${f}" + fi +done + + +# ----------------------------------------------------------------------------- +progress "starting netdata" + +restart_netdata "/opt/netdata/bin/netdata" +if [ $? -eq 0 ] + then + netdata_banner "is installed and running now!" +else + netdata_banner "is installed now!" +fi diff --git a/makeself/jobs/10-prepare-destination.install.sh b/makeself/jobs/10-prepare-destination.install.sh new file mode 100755 index 000000000..58c8c25fd --- /dev/null +++ b/makeself/jobs/10-prepare-destination.install.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +. $(dirname "${0}")/../functions.sh "${@}" || exit 1 + +[ -d "${NETDATA_INSTALL_PATH}.old" ] && run rm -rf "${NETDATA_INSTALL_PATH}.old" +[ -d "${NETDATA_INSTALL_PATH}" ] && run mv -f "${NETDATA_INSTALL_PATH}" "${NETDATA_INSTALL_PATH}.old" + +run mkdir -p "${NETDATA_INSTALL_PATH}/bin" +run mkdir -p "${NETDATA_INSTALL_PATH}/usr" +run cd "${NETDATA_INSTALL_PATH}" +run ln -s bin sbin +run cd "${NETDATA_INSTALL_PATH}/usr" +run ln -s ../bin bin +run ln -s ../sbin sbin +run ln -s . local + diff --git a/makeself/jobs/50-bash-4.4.install.sh b/makeself/jobs/50-bash-4.4.install.sh new file mode 100755 index 000000000..07c84b6d7 --- /dev/null +++ b/makeself/jobs/50-bash-4.4.install.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +. $(dirname "${0}")/../functions.sh "${@}" || exit 1 + +fetch "bash-4.4" "http://ftp.gnu.org/gnu/bash/bash-4.4.tar.gz" + +run ./configure \ + --prefix=${NETDATA_INSTALL_PATH} \ + --enable-static-link \ + --disable-nls \ + --without-bash-malloc \ +# --disable-rpath \ +# --enable-alias \ +# --enable-arith-for-command \ +# --enable-array-variables \ +# --enable-brace-expansion \ +# --enable-casemod-attributes \ +# --enable-casemod-expansions \ +# --enable-command-timing \ +# --enable-cond-command \ +# --enable-cond-regexp \ +# --enable-directory-stack \ +# --enable-dparen-arithmetic \ +# --enable-function-import \ +# --enable-glob-asciiranges-default \ +# --enable-help-builtin \ +# --enable-job-control \ +# --enable-net-redirections \ +# --enable-process-substitution \ +# --enable-progcomp \ +# --enable-prompt-string-decoding \ +# --enable-readline \ +# --enable-select \ + + +run make clean +run make -j${PROCESSORS} + +cat >examples/loadables/Makefile <doc/Makefile <"${NETDATA_INSTALL_PATH}/bin/netdata" <&2 "Self-extracting installer copied to '${FILE}'" + +[ -f netdata-latest.gz.run ] && rm netdata-latest.gz.run +ln -s "${FILE}" netdata-latest.gz.run +echo >&2 "Self-extracting installer linked to 'netdata-latest.gz.run'" diff --git a/makeself/makeself-header.sh b/makeself/makeself-header.sh new file mode 100755 index 000000000..93d937b37 --- /dev/null +++ b/makeself/makeself-header.sh @@ -0,0 +1,554 @@ +cat << EOF > "$archname" +#!/bin/sh +# This script was generated using Makeself $MS_VERSION + +ORIG_UMASK=\`umask\` +if test "$KEEP_UMASK" = n; then + umask 077 +fi + +CRCsum="$CRCsum" +MD5="$MD5sum" +TMPROOT=\${TMPDIR:=/tmp} +USER_PWD="\$PWD"; export USER_PWD + +label="$LABEL" +script="$SCRIPT" +scriptargs="$SCRIPTARGS" +licensetxt="$LICENSE" +helpheader='$HELPHEADER' +targetdir="$archdirname" +filesizes="$filesizes" +keep="$KEEP" +nooverwrite="$NOOVERWRITE" +quiet="n" +accept="n" +nodiskspace="n" +export_conf="$EXPORT_CONF" + +print_cmd_arg="" +if type printf > /dev/null; then + print_cmd="printf" +elif test -x /usr/ucb/echo; then + print_cmd="/usr/ucb/echo" +else + print_cmd="echo" +fi + +if test -d /usr/xpg4/bin; then + PATH=/usr/xpg4/bin:\$PATH + export PATH +fi + +unset CDPATH + +MS_Printf() +{ + \$print_cmd \$print_cmd_arg "\$1" +} + +MS_PrintLicense() +{ + if test x"\$licensetxt" != x; then + echo "\$licensetxt" + if test x"\$accept" != xy; then + while true + do + MS_Printf "Please type y to accept, n otherwise: " + read yn + if test x"\$yn" = xn; then + keep=n + eval \$finish; exit 1 + break; + elif test x"\$yn" = xy; then + break; + fi + done + fi + fi +} + +MS_diskspace() +{ + ( + df -kP "\$1" | tail -1 | awk '{ if (\$4 ~ /%/) {print \$3} else {print \$4} }' + ) +} + +MS_dd() +{ + blocks=\`expr \$3 / 1024\` + bytes=\`expr \$3 % 1024\` + dd if="\$1" ibs=\$2 skip=1 obs=1024 conv=sync 2> /dev/null | \\ + { test \$blocks -gt 0 && dd ibs=1024 obs=1024 count=\$blocks ; \\ + test \$bytes -gt 0 && dd ibs=1 obs=1024 count=\$bytes ; } 2> /dev/null +} + +MS_dd_Progress() +{ + if test x"\$noprogress" = xy; then + MS_dd \$@ + return \$? + fi + file="\$1" + offset=\$2 + length=\$3 + pos=0 + bsize=4194304 + while test \$bsize -gt \$length; do + bsize=\`expr \$bsize / 4\` + done + blocks=\`expr \$length / \$bsize\` + bytes=\`expr \$length % \$bsize\` + ( + dd ibs=\$offset skip=1 2>/dev/null + pos=\`expr \$pos \+ \$bsize\` + MS_Printf " 0%% " 1>&2 + if test \$blocks -gt 0; then + while test \$pos -le \$length; do + dd bs=\$bsize count=1 2>/dev/null + pcent=\`expr \$length / 100\` + pcent=\`expr \$pos / \$pcent\` + if test \$pcent -lt 100; then + MS_Printf "\b\b\b\b\b\b\b" 1>&2 + if test \$pcent -lt 10; then + MS_Printf " \$pcent%% " 1>&2 + else + MS_Printf " \$pcent%% " 1>&2 + fi + fi + pos=\`expr \$pos \+ \$bsize\` + done + fi + if test \$bytes -gt 0; then + dd bs=\$bytes count=1 2>/dev/null + fi + MS_Printf "\b\b\b\b\b\b\b" 1>&2 + MS_Printf " 100%% " 1>&2 + ) < "\$file" +} + +MS_Help() +{ + cat << EOH >&2 +\${helpheader}Makeself version $MS_VERSION + 1) Getting help or info about \$0 : + \$0 --help Print this message + \$0 --info Print embedded info : title, default target directory, embedded script ... + \$0 --lsm Print embedded lsm entry (or no LSM) + \$0 --list Print the list of files in the archive + \$0 --check Checks integrity of the archive + + 2) Running \$0 : + \$0 [options] [--] [additional arguments to embedded script] + with following options (in that order) + --confirm Ask before running embedded script + --quiet Do not print anything except error messages + --accept Accept the license + --noexec Do not run embedded script + --keep Do not erase target directory after running + the embedded script + --noprogress Do not show the progress during the decompression + --nox11 Do not spawn an xterm + --nochown Do not give the extracted files to the current user + --nodiskspace Do not check for available disk space + --target dir Extract directly to a target directory + directory path can be either absolute or relative + --tar arg1 [arg2 ...] Access the contents of the archive through the tar command + -- Following arguments will be passed to the embedded script +EOH +} + +MS_Check() +{ + OLD_PATH="\$PATH" + PATH=\${GUESS_MD5_PATH:-"\$OLD_PATH:/bin:/usr/bin:/sbin:/usr/local/ssl/bin:/usr/local/bin:/opt/openssl/bin"} + MD5_ARG="" + MD5_PATH=\`exec <&- 2>&-; which md5sum || command -v md5sum || type md5sum\` + test -x "\$MD5_PATH" || MD5_PATH=\`exec <&- 2>&-; which md5 || command -v md5 || type md5\` + test -x "\$MD5_PATH" || MD5_PATH=\`exec <&- 2>&-; which digest || command -v digest || type digest\` + PATH="\$OLD_PATH" + + if test x"\$quiet" = xn; then + MS_Printf "Verifying archive integrity..." + fi + offset=\`head -n $SKIP "\$1" | wc -c | tr -d " "\` + verb=\$2 + i=1 + for s in \$filesizes + do + crc=\`echo \$CRCsum | cut -d" " -f\$i\` + if test -x "\$MD5_PATH"; then + if test x"\`basename \$MD5_PATH\`" = xdigest; then + MD5_ARG="-a md5" + fi + md5=\`echo \$MD5 | cut -d" " -f\$i\` + if test x"\$md5" = x00000000000000000000000000000000; then + test x"\$verb" = xy && echo " \$1 does not contain an embedded MD5 checksum." >&2 + else + md5sum=\`MS_dd_Progress "\$1" \$offset \$s | eval "\$MD5_PATH \$MD5_ARG" | cut -b-32\`; + if test x"\$md5sum" != x"\$md5"; then + echo "Error in MD5 checksums: \$md5sum is different from \$md5" >&2 + exit 2 + else + test x"\$verb" = xy && MS_Printf " MD5 checksums are OK." >&2 + fi + crc="0000000000"; verb=n + fi + fi + if test x"\$crc" = x0000000000; then + test x"\$verb" = xy && echo " \$1 does not contain a CRC checksum." >&2 + else + sum1=\`MS_dd_Progress "\$1" \$offset \$s | CMD_ENV=xpg4 cksum | awk '{print \$1}'\` + if test x"\$sum1" = x"\$crc"; then + test x"\$verb" = xy && MS_Printf " CRC checksums are OK." >&2 + else + echo "Error in checksums: \$sum1 is different from \$crc" >&2 + exit 2; + fi + fi + i=\`expr \$i + 1\` + offset=\`expr \$offset + \$s\` + done + if test x"\$quiet" = xn; then + echo " All good." + fi +} + +UnTAR() +{ + if test x"\$quiet" = xn; then + tar \$1 "$UNTAR_EXTRA" -vf - 2>&1 || { echo Extraction failed. > /dev/tty; kill -15 \$$; } + else + + tar \$1 "$UNTAR_EXTRA" -f - 2>&1 || { echo Extraction failed. > /dev/tty; kill -15 \$$; } + fi +} + +finish=true +xterm_loop= +noprogress=$NOPROGRESS +nox11=$NOX11 +copy=$COPY +ownership=y +verbose=n + +initargs="\$@" + +while true +do + case "\$1" in + -h | --help) + MS_Help + exit 0 + ;; + -q | --quiet) + quiet=y + noprogress=y + shift + ;; + --accept) + accept=y + shift + ;; + --info) + echo Identification: "\$label" + echo Target directory: "\$targetdir" + echo Uncompressed size: $USIZE KB + echo Compression: $COMPRESS + echo Date of packaging: $DATE + echo Built with Makeself version $MS_VERSION on $OSTYPE + echo Build command was: "$MS_COMMAND" + if test x"\$script" != x; then + echo Script run after extraction: + echo " " \$script \$scriptargs + fi + if test x"$copy" = xcopy; then + echo "Archive will copy itself to a temporary location" + fi + if test x"$NEED_ROOT" = xy; then + echo "Root permissions required for extraction" + fi + if test x"$KEEP" = xy; then + echo "directory \$targetdir is permanent" + else + echo "\$targetdir will be removed after extraction" + fi + exit 0 + ;; + --dumpconf) + echo LABEL=\"\$label\" + echo SCRIPT=\"\$script\" + echo SCRIPTARGS=\"\$scriptargs\" + echo archdirname=\"$archdirname\" + echo KEEP=$KEEP + echo NOOVERWRITE=$NOOVERWRITE + echo COMPRESS=$COMPRESS + echo filesizes=\"\$filesizes\" + echo CRCsum=\"\$CRCsum\" + echo MD5sum=\"\$MD5\" + echo OLDUSIZE=$USIZE + echo OLDSKIP=`expr $SKIP + 1` + exit 0 + ;; + --lsm) +cat << EOLSM +EOF +eval "$LSM_CMD" +cat << EOF >> "$archname" +EOLSM + exit 0 + ;; + --list) + echo Target directory: \$targetdir + offset=\`head -n $SKIP "\$0" | wc -c | tr -d " "\` + for s in \$filesizes + do + MS_dd "\$0" \$offset \$s | eval "$GUNZIP_CMD" | UnTAR t + offset=\`expr \$offset + \$s\` + done + exit 0 + ;; + --tar) + offset=\`head -n $SKIP "\$0" | wc -c | tr -d " "\` + arg1="\$2" + if ! shift 2; then MS_Help; exit 1; fi + for s in \$filesizes + do + MS_dd "\$0" \$offset \$s | eval "$GUNZIP_CMD" | tar "\$arg1" - "\$@" + offset=\`expr \$offset + \$s\` + done + exit 0 + ;; + --check) + MS_Check "\$0" y + exit 0 + ;; + --confirm) + verbose=y + shift + ;; + --noexec) + script="" + shift + ;; + --keep) + keep=y + shift + ;; + --target) + keep=y + targetdir=\${2:-.} + if ! shift 2; then MS_Help; exit 1; fi + ;; + --noprogress) + noprogress=y + shift + ;; + --nox11) + nox11=y + shift + ;; + --nochown) + ownership=n + shift + ;; + --nodiskspace) + nodiskspace=y + shift + ;; + --xwin) + if test "$NOWAIT" = n; then + finish="echo Press Return to close this window...; read junk" + fi + xterm_loop=1 + shift + ;; + --phase2) + copy=phase2 + shift + ;; + --) + shift + break ;; + -*) + echo Unrecognized flag : "\$1" >&2 + MS_Help + exit 1 + ;; + *) + break ;; + esac +done + +if test x"\$quiet" = xy -a x"\$verbose" = xy; then + echo Cannot be verbose and quiet at the same time. >&2 + exit 1 +fi + +if test x"$NEED_ROOT" = xy -a \`id -u\` -ne 0; then + echo "Administrative privileges required for this archive (use su or sudo)" >&2 + exit 1 +fi + +if test x"\$copy" \!= xphase2; then + MS_PrintLicense +fi + +case "\$copy" in +copy) + tmpdir=\$TMPROOT/makeself.\$RANDOM.\`date +"%y%m%d%H%M%S"\`.\$\$ + mkdir "\$tmpdir" || { + echo "Could not create temporary directory \$tmpdir" >&2 + exit 1 + } + SCRIPT_COPY="\$tmpdir/makeself" + echo "Copying to a temporary location..." >&2 + cp "\$0" "\$SCRIPT_COPY" + chmod +x "\$SCRIPT_COPY" + cd "\$TMPROOT" + exec "\$SCRIPT_COPY" --phase2 -- \$initargs + ;; +phase2) + finish="\$finish ; rm -rf \`dirname \$0\`" + ;; +esac + +if test x"\$nox11" = xn; then + if tty -s; then # Do we have a terminal? + : + else + if test x"\$DISPLAY" != x -a x"\$xterm_loop" = x; then # No, but do we have X? + if xset q > /dev/null 2>&1; then # Check for valid DISPLAY variable + GUESS_XTERMS="xterm gnome-terminal rxvt dtterm eterm Eterm xfce4-terminal lxterminal kvt konsole aterm terminology" + for a in \$GUESS_XTERMS; do + if type \$a >/dev/null 2>&1; then + XTERM=\$a + break + fi + done + chmod a+x \$0 || echo Please add execution rights on \$0 + if test \`echo "\$0" | cut -c1\` = "/"; then # Spawn a terminal! + exec \$XTERM -title "\$label" -e "\$0" --xwin "\$initargs" + else + exec \$XTERM -title "\$label" -e "./\$0" --xwin "\$initargs" + fi + fi + fi + fi +fi + +if test x"\$targetdir" = x.; then + tmpdir="." +else + if test x"\$keep" = xy; then + if test x"\$nooverwrite" = xy && test -d "\$targetdir"; then + echo "Target directory \$targetdir already exists, aborting." >&2 + exit 1 + fi + if test x"\$quiet" = xn; then + echo "Creating directory \$targetdir" >&2 + fi + tmpdir="\$targetdir" + dashp="-p" + else + tmpdir="\$TMPROOT/selfgz\$\$\$RANDOM" + dashp="" + fi + mkdir \$dashp \$tmpdir || { + echo 'Cannot create target directory' \$tmpdir >&2 + echo 'You should try option --target dir' >&2 + eval \$finish + exit 1 + } +fi + +location="\`pwd\`" +if test x"\$SETUP_NOCHECK" != x1; then + MS_Check "\$0" +fi +offset=\`head -n $SKIP "\$0" | wc -c | tr -d " "\` + +if test x"\$verbose" = xy; then + MS_Printf "About to extract $USIZE KB in \$tmpdir ... Proceed ? [Y/n] " + read yn + if test x"\$yn" = xn; then + eval \$finish; exit 1 + fi +fi + +if test x"\$quiet" = xn; then + MS_Printf "Uncompressing \$label" +fi +res=3 +if test x"\$keep" = xn; then + trap 'echo Signal caught, cleaning up >&2; cd \$TMPROOT; /bin/rm -rf \$tmpdir; eval \$finish; exit 15' 1 2 3 15 +fi + +if test x"\$nodiskspace" = xn; then + leftspace=\`MS_diskspace \$tmpdir\` + if test -n "\$leftspace"; then + if test "\$leftspace" -lt $USIZE; then + echo + echo "Not enough space left in "\`dirname \$tmpdir\`" (\$leftspace KB) to decompress \$0 ($USIZE KB)" >&2 + echo "Use --nodiskspace option to skip this check and proceed anyway" >&2 + if test x"\$keep" = xn; then + echo "Consider setting TMPDIR to a directory with more free space." + fi + eval \$finish; exit 1 + fi + fi +fi + +for s in \$filesizes +do + if MS_dd_Progress "\$0" \$offset \$s | eval "$GUNZIP_CMD" | ( cd "\$tmpdir"; umask \$ORIG_UMASK ; UnTAR xp ) 1>/dev/null; then + if test x"\$ownership" = xy; then + (cd "\$tmpdir"; chown -R \`id -u\` .; chgrp -R \`id -g\` .) + fi + else + echo >&2 + echo "Unable to decompress \$0" >&2 + eval \$finish; exit 1 + fi + offset=\`expr \$offset + \$s\` +done +if test x"\$quiet" = xn; then + echo +fi + +cd "\$tmpdir" +res=0 +if test x"\$script" != x; then + if test x"\$export_conf" = x"y"; then + MS_BUNDLE="\$0" + MS_LABEL="\$label" + MS_SCRIPT="\$script" + MS_SCRIPTARGS="\$scriptargs" + MS_ARCHDIRNAME="\$archdirname" + MS_KEEP="\$KEEP" + MS_NOOVERWRITE="\$NOOVERWRITE" + MS_COMPRESS="\$COMPRESS" + export MS_BUNDLE MS_LABEL MS_SCRIPT MS_SCRIPTARGS + export MS_ARCHDIRNAME MS_KEEP MS_NOOVERWRITE MS_COMPRESS + fi + + if test x"\$verbose" = x"y"; then + MS_Printf "OK to execute: \$script \$scriptargs \$* ? [Y/n] " + read yn + if test x"\$yn" = x -o x"\$yn" = xy -o x"\$yn" = xY; then + eval "\"\$script\" \$scriptargs \"\\\$@\""; res=\$?; + fi + else + eval "\"\$script\" \$scriptargs \"\\\$@\""; res=\$? + fi + if test "\$res" -ne 0; then + test x"\$verbose" = xy && echo "The program '\$script' returned an error code (\$res)" >&2 + fi +fi +if test x"\$keep" = xn; then + cd \$TMPROOT + /bin/rm -rf \$tmpdir +fi +eval \$finish; exit \$res +EOF diff --git a/makeself/makeself-help-header.txt b/makeself/makeself-help-header.txt new file mode 100644 index 000000000..e26490059 --- /dev/null +++ b/makeself/makeself-help-header.txt @@ -0,0 +1,46 @@ + + ^ + |.-. .-. .-. .-. . netdata + | '-' '-' '-' '-' real-time performance monitoring, done right! + +----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+---> + + (C) Copyright 2017, Costa Tsaousis + All rights reserved + Released under GPL v3+ + + You are about to install netdata to this system. + netdata will be installed at: + + /opt/netdata + + The following changes will be made to your system: + + # USERS / GROUPS + User 'netdata' and group 'netdata' will be added, if not present. + + # LOGROTATE + This file will be installed if logrotate is present. + + - /etc/logrotate.d/netdata + + # SYSTEM INIT + This file will be installed if this system runs with systemd: + + - /etc/systemd/system/netdata.service + + or, for older Centos, Debian/Ubuntu or OpenRC Gentoo: + + - /etc/init.d/netdata will be created + + + This package can also update a netdata installation that has been + created with another version of it. + + Your netdata configuration will be retained. + After installation, netdata will be (re-)started. + + netdata re-distributes a lot of open source software components. + Check its full license at: + https://github.com/firehol/netdata/blob/master/LICENSE.md + + diff --git a/makeself/makeself-license.txt b/makeself/makeself-license.txt new file mode 100644 index 000000000..e26490059 --- /dev/null +++ b/makeself/makeself-license.txt @@ -0,0 +1,46 @@ + + ^ + |.-. .-. .-. .-. . netdata + | '-' '-' '-' '-' real-time performance monitoring, done right! + +----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+---> + + (C) Copyright 2017, Costa Tsaousis + All rights reserved + Released under GPL v3+ + + You are about to install netdata to this system. + netdata will be installed at: + + /opt/netdata + + The following changes will be made to your system: + + # USERS / GROUPS + User 'netdata' and group 'netdata' will be added, if not present. + + # LOGROTATE + This file will be installed if logrotate is present. + + - /etc/logrotate.d/netdata + + # SYSTEM INIT + This file will be installed if this system runs with systemd: + + - /etc/systemd/system/netdata.service + + or, for older Centos, Debian/Ubuntu or OpenRC Gentoo: + + - /etc/init.d/netdata will be created + + + This package can also update a netdata installation that has been + created with another version of it. + + Your netdata configuration will be retained. + After installation, netdata will be (re-)started. + + netdata re-distributes a lot of open source software components. + Check its full license at: + https://github.com/firehol/netdata/blob/master/LICENSE.md + + diff --git a/makeself/makeself.lsm b/makeself/makeself.lsm new file mode 100644 index 000000000..026796294 --- /dev/null +++ b/makeself/makeself.lsm @@ -0,0 +1,16 @@ +Begin3 +Title: netdata +Version: 1.6.0 +Description: netdata is a system for distributed real-time performance and health monitoring. + It provides unparalleled insights, in real-time, of everything happening on the + system it runs (including applications such as web and database servers), using + modern interactive web dashboards. netdata is fast and efficient, designed to + permanently run on all systems (physical & virtual servers, containers, IoT + devices), without disrupting their core function. +Keywords: real-time performance and health monitoring +Author: Costa Tsaousis (costa@tsaousis.gr) +Maintained-by: Costa Tsaousis (costa@tsaousis.gr) +Original-site: https://my-netdata.io/ +Platform: Unix +Copying-policy: GPL +End diff --git a/makeself/makeself.sh b/makeself/makeself.sh new file mode 100755 index 000000000..709473aab --- /dev/null +++ b/makeself/makeself.sh @@ -0,0 +1,620 @@ +#!/bin/sh +# +# Makeself version 2.3.x +# by Stephane Peter +# +# Utility to create self-extracting tar.gz archives. +# The resulting archive is a file holding the tar.gz archive with +# a small Shell script stub that uncompresses the archive to a temporary +# directory and then executes a given script from withing that directory. +# +# Makeself home page: http://makeself.io/ +# +# Version 2.0 is a rewrite of version 1.0 to make the code easier to read and maintain. +# +# Version history : +# - 1.0 : Initial public release +# - 1.1 : The archive can be passed parameters that will be passed on to +# the embedded script, thanks to John C. Quillan +# - 1.2 : Package distribution, bzip2 compression, more command line options, +# support for non-temporary archives. Ideas thanks to Francois Petitjean +# - 1.3 : More patches from Bjarni R. Einarsson and Francois Petitjean: +# Support for no compression (--nocomp), script is no longer mandatory, +# automatic launch in an xterm, optional verbose output, and -target +# archive option to indicate where to extract the files. +# - 1.4 : Improved UNIX compatibility (Francois Petitjean) +# Automatic integrity checking, support of LSM files (Francois Petitjean) +# - 1.5 : Many bugfixes. Optionally disable xterm spawning. +# - 1.5.1 : More bugfixes, added archive options -list and -check. +# - 1.5.2 : Cosmetic changes to inform the user of what's going on with big +# archives (Quake III demo) +# - 1.5.3 : Check for validity of the DISPLAY variable before launching an xterm. +# More verbosity in xterms and check for embedded command's return value. +# Bugfix for Debian 2.0 systems that have a different "print" command. +# - 1.5.4 : Many bugfixes. Print out a message if the extraction failed. +# - 1.5.5 : More bugfixes. Added support for SETUP_NOCHECK environment variable to +# bypass checksum verification of archives. +# - 1.6.0 : Compute MD5 checksums with the md5sum command (patch from Ryan Gordon) +# - 2.0 : Brand new rewrite, cleaner architecture, separated header and UNIX ports. +# - 2.0.1 : Added --copy +# - 2.1.0 : Allow multiple tarballs to be stored in one archive, and incremental updates. +# Added --nochown for archives +# Stopped doing redundant checksums when not necesary +# - 2.1.1 : Work around insane behavior from certain Linux distros with no 'uncompress' command +# Cleaned up the code to handle error codes from compress. Simplified the extraction code. +# - 2.1.2 : Some bug fixes. Use head -n to avoid problems. +# - 2.1.3 : Bug fixes with command line when spawning terminals. +# Added --tar for archives, allowing to give arbitrary arguments to tar on the contents of the archive. +# Added --noexec to prevent execution of embedded scripts. +# Added --nomd5 and --nocrc to avoid creating checksums in archives. +# Added command used to create the archive in --info output. +# Run the embedded script through eval. +# - 2.1.4 : Fixed --info output. +# Generate random directory name when extracting files to . to avoid problems. (Jason Trent) +# Better handling of errors with wrong permissions for the directory containing the files. (Jason Trent) +# Avoid some race conditions (Ludwig Nussel) +# Unset the $CDPATH variable to avoid problems if it is set. (Debian) +# Better handling of dot files in the archive directory. +# - 2.1.5 : Made the md5sum detection consistent with the header code. +# Check for the presence of the archive directory +# Added --encrypt for symmetric encryption through gpg (Eric Windisch) +# Added support for the digest command on Solaris 10 for MD5 checksums +# Check for available disk space before extracting to the target directory (Andreas Schweitzer) +# Allow extraction to run asynchronously (patch by Peter Hatch) +# Use file descriptors internally to avoid error messages (patch by Kay Tiong Khoo) +# - 2.1.6 : Replaced one dot per file progress with a realtime progress percentage and a spining cursor (Guy Baconniere) +# Added --noprogress to prevent showing the progress during the decompression (Guy Baconniere) +# Added --target dir to allow extracting directly to a target directory (Guy Baconniere) +# - 2.2.0 : Many bugfixes, updates and contributions from users. Check out the project page on Github for the details. +# - 2.3.0 : Option to specify packaging date to enable byte-for-byte reproducibility. (Marc Pawlowsky) +# +# (C) 1998-2017 by Stephane Peter +# +# This software is released under the terms of the GNU GPL version 2 and above +# Please read the license at http://www.gnu.org/copyleft/gpl.html +# + +MS_VERSION=2.3.1 +MS_COMMAND="$0" +unset CDPATH + +for f in "${1+"$@"}"; do + MS_COMMAND="$MS_COMMAND \\\\ + \\\"$f\\\"" +done + +# For Solaris systems +if test -d /usr/xpg4/bin; then + PATH=/usr/xpg4/bin:$PATH + export PATH +fi + +# Procedures + +MS_Usage() +{ + echo "Usage: $0 [params] archive_dir file_name label startup_script [args]" + echo "params can be one or more of the following :" + echo " --version | -v : Print out Makeself version number and exit" + echo " --help | -h : Print out this help message" + echo " --tar-quietly : Suppress verbose output from the tar command" + echo " --quiet | -q : Do not print any messages other than errors." + echo " --gzip : Compress using gzip (default if detected)" + echo " --pigz : Compress with pigz" + echo " --bzip2 : Compress using bzip2 instead of gzip" + echo " --pbzip2 : Compress using pbzip2 instead of gzip" + echo " --xz : Compress using xz instead of gzip" + echo " --lzo : Compress using lzop instead of gzip" + echo " --lz4 : Compress using lz4 instead of gzip" + echo " --compress : Compress using the UNIX 'compress' command" + echo " --complevel lvl : Compression level for gzip pigz xz lzo lz4 bzip2 and pbzip2 (default 9)" + echo " --base64 : Instead of compressing, encode the data using base64" + echo " --gpg-encrypt : Instead of compressing, encrypt the data using GPG" + echo " --gpg-asymmetric-encrypt-sign" + echo " : Instead of compressing, asymmetrically encrypt and sign the data using GPG" + echo " --gpg-extra opt : Append more options to the gpg command line" + echo " --ssl-encrypt : Instead of compressing, encrypt the data using OpenSSL" + echo " --nocomp : Do not compress the data" + echo " --notemp : The archive will create archive_dir in the" + echo " current directory and uncompress in ./archive_dir" + echo " --needroot : Check that the root user is extracting the archive before proceeding" + echo " --copy : Upon extraction, the archive will first copy itself to" + echo " a temporary directory" + echo " --append : Append more files to an existing Makeself archive" + echo " The label and startup scripts will then be ignored" + echo " --target dir : Extract directly to a target directory" + echo " directory path can be either absolute or relative" + echo " --nooverwrite : Do not extract the archive if the specified target directory exists" + echo " --current : Files will be extracted to the current directory" + echo " Both --current and --target imply --notemp" + echo " --tar-extra opt : Append more options to the tar command line" + echo " --untar-extra opt : Append more options to the during the extraction of the tar archive" + echo " --nomd5 : Don't calculate an MD5 for archive" + echo " --nocrc : Don't calculate a CRC for archive" + echo " --header file : Specify location of the header script" + echo " --follow : Follow the symlinks in the archive" + echo " --noprogress : Do not show the progress during the decompression" + echo " --nox11 : Disable automatic spawn of a xterm" + echo " --nowait : Do not wait for user input after executing embedded" + echo " program from an xterm" + echo " --lsm file : LSM file describing the package" + echo " --license file : Append a license file" + echo " --help-header file : Add a header to the archive's --help output" + echo " --packaging-date date" + echo " : Use provided string as the packaging date" + echo " instead of the current date." + echo + echo " --keep-umask : Keep the umask set to shell default, rather than overriding when executing self-extracting archive." + echo " --export-conf : Export configuration variables to startup_script" + echo + echo "Do not forget to give a fully qualified startup script name" + echo "(i.e. with a ./ prefix if inside the archive)." + exit 1 +} + +# Default settings +if type gzip 2>&1 > /dev/null; then + COMPRESS=gzip +else + COMPRESS=Unix +fi +COMPRESS_LEVEL=9 +KEEP=n +CURRENT=n +NOX11=n +NOWAIT=n +APPEND=n +TAR_QUIETLY=n +KEEP_UMASK=n +QUIET=n +NOPROGRESS=n +COPY=none +NEED_ROOT=n +TAR_ARGS=cvf +TAR_EXTRA="" +GPG_EXTRA="" +DU_ARGS=-ks +HEADER=`dirname "$0"`/makeself-header.sh +TARGETDIR="" +NOOVERWRITE=n +DATE=`LC_ALL=C date` +EXPORT_CONF=n + +# LSM file stuff +LSM_CMD="echo No LSM. >> \"\$archname\"" + +while true +do + case "$1" in + --version | -v) + echo Makeself version $MS_VERSION + exit 0 + ;; + --pbzip2) + COMPRESS=pbzip2 + shift + ;; + --bzip2) + COMPRESS=bzip2 + shift + ;; + --gzip) + COMPRESS=gzip + shift + ;; + --pigz) + COMPRESS=pigz + shift + ;; + --xz) + COMPRESS=xz + shift + ;; + --lzo) + COMPRESS=lzo + shift + ;; + --lz4) + COMPRESS=lz4 + shift + ;; + --compress) + COMPRESS=Unix + shift + ;; + --base64) + COMPRESS=base64 + shift + ;; + --gpg-encrypt) + COMPRESS=gpg + shift + ;; + --gpg-asymmetric-encrypt-sign) + COMPRESS=gpg-asymmetric + shift + ;; + --gpg-extra) + GPG_EXTRA="$2" + if ! shift 2; then MS_Help; exit 1; fi + ;; + --ssl-encrypt) + COMPRESS=openssl + shift + ;; + --nocomp) + COMPRESS=none + shift + ;; + --complevel) + COMPRESS_LEVEL="$2" + if ! shift 2; then MS_Help; exit 1; fi + ;; + --notemp) + KEEP=y + shift + ;; + --copy) + COPY=copy + shift + ;; + --current) + CURRENT=y + KEEP=y + shift + ;; + --tar-extra) + TAR_EXTRA="$2" + if ! shift 2; then MS_Help; exit 1; fi + ;; + --untar-extra) + UNTAR_EXTRA="$2" + if ! shift 2; then MS_Help; exit 1; fi + ;; + --target) + TARGETDIR="$2" + KEEP=y + if ! shift 2; then MS_Help; exit 1; fi + ;; + --nooverwrite) + NOOVERWRITE=y + shift + ;; + --needroot) + NEED_ROOT=y + shift + ;; + --header) + HEADER="$2" + if ! shift 2; then MS_Help; exit 1; fi + ;; + --license) + LICENSE=`cat $2` + if ! shift 2; then MS_Help; exit 1; fi + ;; + --follow) + TAR_ARGS=cvhf + DU_ARGS=-ksL + shift + ;; + --noprogress) + NOPROGRESS=y + shift + ;; + --nox11) + NOX11=y + shift + ;; + --nowait) + NOWAIT=y + shift + ;; + --nomd5) + NOMD5=y + shift + ;; + --nocrc) + NOCRC=y + shift + ;; + --append) + APPEND=y + shift + ;; + --lsm) + LSM_CMD="cat \"$2\" >> \"\$archname\"" + if ! shift 2; then MS_Help; exit 1; fi + ;; + --packaging-date) + DATE="$2" + if ! shift 2; then MS_Help; exit 1; fi + ;; + --help-header) + HELPHEADER=`sed -e "s/'/'\\\\\''/g" $2` + if ! shift 2; then MS_Help; exit 1; fi + [ -n "$HELPHEADER" ] && HELPHEADER="$HELPHEADER +" + ;; + --tar-quietly) + TAR_QUIETLY=y + shift + ;; + --keep-umask) + KEEP_UMASK=y + shift + ;; + --export-conf) + EXPORT_CONF=y + shift + ;; + -q | --quiet) + QUIET=y + shift + ;; + -h | --help) + MS_Usage + ;; + -*) + echo Unrecognized flag : "$1" + MS_Usage + ;; + *) + break + ;; + esac +done + +if test $# -lt 1; then + MS_Usage +else + if test -d "$1"; then + archdir="$1" + else + echo "Directory $1 does not exist." >&2 + exit 1 + fi +fi +archname="$2" + +if test "$QUIET" = "y" || test "$TAR_QUIETLY" = "y"; then + if test "$TAR_ARGS" = "cvf"; then + TAR_ARGS="cf" + elif test "$TAR_ARGS" = "cvhf";then + TAR_ARGS="chf" + fi +fi + +if test "$APPEND" = y; then + if test $# -lt 2; then + MS_Usage + fi + + # Gather the info from the original archive + OLDENV=`sh "$archname" --dumpconf` + if test $? -ne 0; then + echo "Unable to update archive: $archname" >&2 + exit 1 + else + eval "$OLDENV" + fi +else + if test "$KEEP" = n -a $# = 3; then + echo "ERROR: Making a temporary archive with no embedded command does not make sense!" >&2 + echo >&2 + MS_Usage + fi + # We don't want to create an absolute directory unless a target directory is defined + if test "$CURRENT" = y; then + archdirname="." + elif test x$TARGETDIR != x; then + archdirname="$TARGETDIR" + else + archdirname=`basename "$1"` + fi + + if test $# -lt 3; then + MS_Usage + fi + + LABEL="$3" + SCRIPT="$4" + test "x$SCRIPT" = x || shift 1 + shift 3 + SCRIPTARGS="$*" +fi + +if test "$KEEP" = n -a "$CURRENT" = y; then + echo "ERROR: It is A VERY DANGEROUS IDEA to try to combine --notemp and --current." >&2 + exit 1 +fi + +case $COMPRESS in +gzip) + GZIP_CMD="gzip -c$COMPRESS_LEVEL" + GUNZIP_CMD="gzip -cd" + ;; +pigz) + GZIP_CMD="pigz -$COMPRESS_LEVEL" + GUNZIP_CMD="gzip -cd" + ;; +pbzip2) + GZIP_CMD="pbzip2 -c$COMPRESS_LEVEL" + GUNZIP_CMD="bzip2 -d" + ;; +bzip2) + GZIP_CMD="bzip2 -$COMPRESS_LEVEL" + GUNZIP_CMD="bzip2 -d" + ;; +xz) + GZIP_CMD="xz -c$COMPRESS_LEVEL" + GUNZIP_CMD="xz -d" + ;; +lzo) + GZIP_CMD="lzop -c$COMPRESS_LEVEL" + GUNZIP_CMD="lzop -d" + ;; +lz4) + GZIP_CMD="lz4 -c$COMPRESS_LEVEL" + GUNZIP_CMD="lz4 -d" + ;; +base64) + GZIP_CMD="base64" + GUNZIP_CMD="base64 -d -i" + ;; +gpg) + GZIP_CMD="gpg $GPG_EXTRA -ac -z$COMPRESS_LEVEL" + GUNZIP_CMD="gpg -d" + ;; +gpg-asymmetric) + GZIP_CMD="gpg $GPG_EXTRA -z$COMPRESS_LEVEL -es" + GUNZIP_CMD="gpg --yes -d" + ;; +openssl) + GZIP_CMD="openssl aes-256-cbc -a -salt" + GUNZIP_CMD="openssl aes-256-cbc -d -a" + ;; +Unix) + GZIP_CMD="compress -cf" + GUNZIP_CMD="exec 2>&-; uncompress -c || test \\\$? -eq 2 || gzip -cd" + ;; +none) + GZIP_CMD="cat" + GUNZIP_CMD="cat" + ;; +esac + +tmpfile="${TMPDIR:=/tmp}/mkself$$" + +if test -f "$HEADER"; then + oldarchname="$archname" + archname="$tmpfile" + # Generate a fake header to count its lines + SKIP=0 + . "$HEADER" + SKIP=`cat "$tmpfile" |wc -l` + # Get rid of any spaces + SKIP=`expr $SKIP` + rm -f "$tmpfile" + if test "$QUIET" = "n";then + echo Header is $SKIP lines long >&2 + fi + + archname="$oldarchname" +else + echo "Unable to open header file: $HEADER" >&2 + exit 1 +fi + +if test "$QUIET" = "n";then + echo +fi + +if test "$APPEND" = n; then + if test -f "$archname"; then + echo "WARNING: Overwriting existing file: $archname" >&2 + fi +fi + +USIZE=`du $DU_ARGS "$archdir" | awk '{print $1}'` + +if test "." = "$archdirname"; then + if test "$KEEP" = n; then + archdirname="makeself-$$-`date +%Y%m%d%H%M%S`" + fi +fi + +test -d "$archdir" || { echo "Error: $archdir does not exist."; rm -f "$tmpfile"; exit 1; } +if test "$QUIET" = "n";then + echo About to compress $USIZE KB of data... + echo Adding files to archive named \"$archname\"... +fi +exec 3<> "$tmpfile" +(cd "$archdir" && ( tar "$TAR_EXTRA" -$TAR_ARGS - . | eval "$GZIP_CMD" >&3 ) ) || { echo Aborting: Archive directory not found or temporary file: "$tmpfile" could not be created.; exec 3>&-; rm -f "$tmpfile"; exit 1; } +exec 3>&- # try to close the archive + +fsize=`cat "$tmpfile" | wc -c | tr -d " "` + +# Compute the checksums + +md5sum=00000000000000000000000000000000 +crcsum=0000000000 + +if test "$NOCRC" = y; then + if test "$QUIET" = "n";then + echo "skipping crc at user request" + fi +else + crcsum=`cat "$tmpfile" | CMD_ENV=xpg4 cksum | sed -e 's/ /Z/' -e 's/ /Z/' | cut -dZ -f1` + if test "$QUIET" = "n";then + echo "CRC: $crcsum" + fi +fi + +if test "$NOMD5" = y; then + if test "$QUIET" = "n";then + echo "skipping md5sum at user request" + fi +else + # Try to locate a MD5 binary + OLD_PATH=$PATH + PATH=${GUESS_MD5_PATH:-"$OLD_PATH:/bin:/usr/bin:/sbin:/usr/local/ssl/bin:/usr/local/bin:/opt/openssl/bin"} + MD5_ARG="" + MD5_PATH=`exec <&- 2>&-; which md5sum || command -v md5sum || type md5sum` + test -x "$MD5_PATH" || MD5_PATH=`exec <&- 2>&-; which md5 || command -v md5 || type md5` + test -x "$MD5_PATH" || MD5_PATH=`exec <&- 2>&-; which digest || command -v digest || type digest` + PATH=$OLD_PATH + if test -x "$MD5_PATH"; then + if test `basename ${MD5_PATH}`x = digestx; then + MD5_ARG="-a md5" + fi + md5sum=`cat "$tmpfile" | eval "$MD5_PATH $MD5_ARG" | cut -b-32`; + if test "$QUIET" = "n";then + echo "MD5: $md5sum" + fi + else + if test "$QUIET" = "n";then + echo "MD5: none, MD5 command not found" + fi + fi +fi + +if test "$APPEND" = y; then + mv "$archname" "$archname".bak || exit + + # Prepare entry for new archive + filesizes="$filesizes $fsize" + CRCsum="$CRCsum $crcsum" + MD5sum="$MD5sum $md5sum" + USIZE=`expr $USIZE + $OLDUSIZE` + # Generate the header + . "$HEADER" + # Append the original data + tail -n +$OLDSKIP "$archname".bak >> "$archname" + # Append the new data + cat "$tmpfile" >> "$archname" + + chmod +x "$archname" + rm -f "$archname".bak + if test "$QUIET" = "n";then + echo Self-extractable archive \"$archname\" successfully updated. + fi +else + filesizes="$fsize" + CRCsum="$crcsum" + MD5sum="$md5sum" + + # Generate the header + . "$HEADER" + + # Append the compressed tar data after the stub + if test "$QUIET" = "n";then + echo + fi + cat "$tmpfile" >> "$archname" + chmod +x "$archname" + if test "$QUIET" = "n";then + echo Self-extractable archive \"$archname\" successfully created. + fi +fi +rm -f "$tmpfile" + diff --git a/makeself/post-installer.sh b/makeself/post-installer.sh new file mode 100755 index 000000000..10f9863b9 --- /dev/null +++ b/makeself/post-installer.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# This script is started using the shell of the system +# and executes our 'install-or-update.sh' script +# using the netdata supplied, statically linked BASH +# +# so, at 'install-or-update.sh' we are always sure +# we run under BASH v4. + +./bin/bash system/install-or-update.sh "${@}" diff --git a/makeself/run-all-jobs.sh b/makeself/run-all-jobs.sh new file mode 100755 index 000000000..b08fa9187 --- /dev/null +++ b/makeself/run-all-jobs.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +LC_ALL=C +umask 002 + +# be nice +renice 19 $$ >/dev/null 2>/dev/null + +# ----------------------------------------------------------------------------- +# prepare the environment for the jobs + +# installation directory +export NETDATA_INSTALL_PATH="${1-/opt/netdata}" + +# our source directory +export NETDATA_MAKESELF_PATH="$(dirname "${0}")" +if [ "${NETDATA_MAKESELF_PATH:0:1}" != "/" ] + then + export NETDATA_MAKESELF_PATH="$(pwd)/${NETDATA_MAKESELF_PATH}" +fi + +# netdata source directory +export NETDATA_SOURCE_PATH="${NETDATA_MAKESELF_PATH}/.." + +# number of processors this system has +PROCESSORS=$(cat /proc/cpuinfo 2>/dev/null | grep ^processor | wc -l) +[ -z "${PROCESSORS}" -o $(( PROCESSORS )) -lt 1 ] && PROCESSORS=1 +export PROCESSORS + +# make sure ${NULL} is empty +export NULL= + +# ----------------------------------------------------------------------------- + +cd "${NETDATA_MAKESELF_PATH}" || exit 1 + +. ./functions.sh "${@}" || exit 1 + +for x in jobs/*.install.sh +do + progress "running ${x}" + "${x}" "${NETDATA_INSTALL_PATH}" +done + +echo >&2 "All jobs for static packaging done successfully." +exit 0 \ No newline at end of file diff --git a/makeself/setup-x86_64-static.sh b/makeself/setup-x86_64-static.sh new file mode 100755 index 000000000..87cd29669 --- /dev/null +++ b/makeself/setup-x86_64-static.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env sh + +# this script should be running in alpine linux +# install the required packages +apk update +apk add --no-cache \ + bash \ + wget \ + curl \ + ncurses \ + git \ + netcat-openbsd \ + alpine-sdk \ + autoconf \ + automake \ + gcc \ + make \ + libtool \ + pkgconfig \ + util-linux-dev \ + openssl-dev \ + gnutls-dev \ + zlib-dev \ + libmnl-dev \ + libnetfilter_acct-dev \ + || exit 1 diff --git a/missing b/missing index 86a8fc31e..db98974ff 100755 --- a/missing +++ b/missing @@ -1,11 +1,10 @@ #! /bin/sh -# Common stub for a few missing GNU programs while installing. +# Common wrapper for a few potentially missing GNU programs. -scriptversion=2012-01-06.13; # UTC +scriptversion=2013-10-28.13; # UTC -# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, -# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. -# Originally by Fran,cois Pinard , 1996. +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,68 +25,40 @@ scriptversion=2012-01-06.13; # UTC # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then - echo 1>&2 "Try \`$0 --help' for more information" + echo 1>&2 "Try '$0 --help' for more information" exit 1 fi -run=: -sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' -sed_minuso='s/.* -o \([^ ]*\).*/\1/p' - -# In the cases where this matters, `missing' is being run in the -# srcdir already. -if test -f configure.ac; then - configure_ac=configure.ac -else - configure_ac=configure.in -fi +case $1 in -msg="missing on your system" + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; -case $1 in ---run) - # Try to run requested program, and just exit if it succeeds. - run= - shift - "$@" && exit 0 - # Exit code 63 means version mismatch. This often happens - # when the user try to use an ancient version of a tool on - # a file that requires a minimum version. In this case we - # we should proceed has if the program had been absent, or - # if --run hadn't been passed. - if test $? = 63; then - run=: - msg="probably too old" - fi - ;; + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... -Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an -error status if there is no known handling for PROGRAM. +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit - --run try to run the given command, and emulate it if it fails Supported PROGRAM values: - aclocal touch file \`aclocal.m4' - autoconf touch file \`configure' - autoheader touch file \`config.h.in' - autom4te touch the output file, or create a stub one - automake touch all \`Makefile.in' files - bison create \`y.tab.[ch]', if possible, from existing .[ch] - flex create \`lex.yy.c', if possible, from existing .c - help2man touch the output file - lex create \`lex.yy.c', if possible, from existing .c - makeinfo touch the output file - yacc create \`y.tab.[ch]', if possible, from existing .[ch] + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man -Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and -\`g' are ignored when checking the name. +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. Send bug reports to ." exit $? @@ -99,228 +70,141 @@ Send bug reports to ." ;; -*) - echo 1>&2 "$0: Unknown \`$1' option" - echo 1>&2 "Try \`$0 --help' for more information" + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac -# normalize program name to check for. -program=`echo "$1" | sed ' - s/^gnu-//; t - s/^gnu//; t - s/^g//; t'` - -# Now exit if we have it, but it failed. Also exit now if we -# don't have it and --version was passed (most likely to detect -# the program). This is about non-GNU programs, so use $1 not -# $program. -case $1 in - lex*|yacc*) - # Not GNU programs, they don't have --version. - ;; - - *) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - elif test "x$2" = "x--version" || test "x$2" = "x--help"; then - # Could not run --version or --help. This is probably someone - # running `$TOOL --version' or `$TOOL --help' to check whether - # $TOOL exists and not knowing $TOOL uses missing. - exit 1 - fi - ;; -esac - -# If it does not exist, or fails to run (possibly an outdated version), -# try to emulate it. -case $program in - aclocal*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`acinclude.m4' or \`${configure_ac}'. You might want - to install the \`Automake' and \`Perl' packages. Grab them from - any GNU archive site." - touch aclocal.m4 - ;; - - autoconf*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`${configure_ac}'. You might want to install the - \`Autoconf' and \`GNU m4' packages. Grab them from any GNU - archive site." - touch configure - ;; - - autoheader*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`acconfig.h' or \`${configure_ac}'. You might want - to install the \`Autoconf' and \`GNU m4' packages. Grab them - from any GNU archive site." - files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` - test -z "$files" && files="config.h" - touch_files= - for f in $files; do - case $f in - *:*) touch_files="$touch_files "`echo "$f" | - sed -e 's/^[^:]*://' -e 's/:.*//'`;; - *) touch_files="$touch_files $f.in";; - esac - done - touch $touch_files - ;; - - automake*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. - You might want to install the \`Automake' and \`Perl' packages. - Grab them from any GNU archive site." - find . -type f -name Makefile.am -print | - sed 's/\.am$/.in/' | - while read f; do touch "$f"; done - ;; - - autom4te*) - echo 1>&2 "\ -WARNING: \`$1' is needed, but is $msg. - You might have modified some files without having the - proper tools for further handling them. - You can get \`$1' as part of \`Autoconf' from any GNU - archive site." - - file=`echo "$*" | sed -n "$sed_output"` - test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` - if test -f "$file"; then - touch $file - else - test -z "$file" || exec >$file - echo "#! /bin/sh" - echo "# Created by GNU Automake missing as a replacement of" - echo "# $ $@" - echo "exit 0" - chmod +x $file - exit 1 - fi - ;; - - bison*|yacc*) - echo 1>&2 "\ -WARNING: \`$1' $msg. You should only need it if - you modified a \`.y' file. You may need the \`Bison' package - in order for those modifications to take effect. You can get - \`Bison' from any GNU archive site." - rm -f y.tab.c y.tab.h - if test $# -ne 1; then - eval LASTARG=\${$#} - case $LASTARG in - *.y) - SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` - if test -f "$SRCFILE"; then - cp "$SRCFILE" y.tab.c - fi - SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` - if test -f "$SRCFILE"; then - cp "$SRCFILE" y.tab.h - fi - ;; - esac - fi - if test ! -f y.tab.h; then - echo >y.tab.h - fi - if test ! -f y.tab.c; then - echo 'main() { return 0; }' >y.tab.c - fi - ;; - - lex*|flex*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a \`.l' file. You may need the \`Flex' package - in order for those modifications to take effect. You can get - \`Flex' from any GNU archive site." - rm -f lex.yy.c - if test $# -ne 1; then - eval LASTARG=\${$#} - case $LASTARG in - *.l) - SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` - if test -f "$SRCFILE"; then - cp "$SRCFILE" lex.yy.c - fi - ;; - esac - fi - if test ! -f lex.yy.c; then - echo 'main() { return 0; }' >lex.yy.c - fi - ;; - - help2man*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a dependency of a manual page. You may need the - \`Help2man' package in order for those modifications to take - effect. You can get \`Help2man' from any GNU archive site." - - file=`echo "$*" | sed -n "$sed_output"` - test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` - if test -f "$file"; then - touch $file - else - test -z "$file" || exec >$file - echo ".ab help2man is required to generate this page" - exit $? - fi - ;; - - makeinfo*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a \`.texi' or \`.texinfo' file, or any other file - indirectly affecting the aspect of the manual. The spurious - call might also be the consequence of using a buggy \`make' (AIX, - DU, IRIX). You might want to install the \`Texinfo' package or - the \`GNU make' package. Grab either from any GNU archive site." - # The file to touch is that specified with -o ... - file=`echo "$*" | sed -n "$sed_output"` - test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` - if test -z "$file"; then - # ... or it is the one specified with @setfilename ... - infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` - file=`sed -n ' - /^@setfilename/{ - s/.* \([^ ]*\) *$/\1/ - p - q - }' $infile` - # ... or it is derived from the source name (dir/f.texi becomes f.info) - test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info - fi - # If the file does not exist, the user really needs makeinfo; - # let's fail without touching anything. - test -f $file || exit 1 - touch $file - ;; +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi - *) - echo 1>&2 "\ -WARNING: \`$1' is needed, and is $msg. - You might have modified some files without having the - proper tools for further handling them. Check the \`README' file, - it often tells you about the needed prerequisites for installing - this package. You may also peek at any GNU archive site, in case - some other package would contain this missing \`$1' program." - exit 1 +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" ;; -esac - -exit 0 + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) diff --git a/netdata-installer.sh b/netdata-installer.sh index 6b672a242..35cb850fb 100755 --- a/netdata-installer.sh +++ b/netdata-installer.sh @@ -7,7 +7,7 @@ installer_dir="$(dirname "${0}")" if [ "${netdata_source_dir}" != "${installer_dir}" -a "${installer_dir}" != "." ] then - echo >&2 "Warninng: you are currently in '${netdata_source_dir}' but the installer is in '${installer_dir}'." + echo >&2 "Warning: you are currently in '${netdata_source_dir}' but the installer is in '${installer_dir}'." fi @@ -43,7 +43,7 @@ then export ACLOCAL_PATH fi -LC_ALL=C +export LC_ALL=C umask 002 # Be nice on production environments @@ -65,12 +65,16 @@ printf "\n" >>netdata-installer.log REINSTALL_PWD="${PWD}" REINSTALL_COMMAND="$(printf "%q " "$0" "${@}"; printf "\n")" +# remove options that shown not be inherited by netdata-updater.sh +REINSTALL_COMMAND="${REINSTALL_COMMAND// --dont-wait/}" +REINSTALL_COMMAND="${REINSTALL_COMMAND// --dont-start-it/}" setcap="$(which setcap 2>/dev/null || command -v setcap 2>/dev/null)" ME="$0" DONOTSTART=0 DONOTWAIT=0 +AUTOUPDATE=0 NETDATA_PREFIX= LIBS_ARE_HERE=0 NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS-}" @@ -98,6 +102,12 @@ Valid are: Do not wait for the user to press ENTER. Start immediately building it. + --auto-update | -u + + Install netdata-updater to cron, + to update netdata automatically once per day + (can only be done for installations from git) + --enable-plugin-freeipmi --disable-plugin-freeipmi @@ -138,7 +148,7 @@ these packages installed: For the plugins, you will at least need: - curl nodejs + curl, bash v4+, python v2 or v3, node.js USAGE } @@ -203,6 +213,10 @@ do then DONOTWAIT=1 shift 1 + elif [ "$1" = "--auto-update" -o "$1" = "-u" ] + then + AUTOUPDATE=1 + shift 1 elif [ "$1" = "--enable-plugin-freeipmi" ] then NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS} --enable-plugin-freeipmi" @@ -541,7 +555,7 @@ config_signature_matches() { # backup user configurations installer_backup_suffix="${PID}.${RANDOM}" -for x in $(find "${NETDATA_PREFIX}/etc/netdata/" -name '*.conf' -type f) +for x in $(find -L "${NETDATA_PREFIX}/etc/netdata/" -name '*.conf' -type f) do if [ -f "${x}" ] then @@ -552,7 +566,7 @@ do then # we don't have md5sum - keep it echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' ${TPUT_RET}is not known to distribution${TPUT_RESET}. Keeping it." - run cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}" + run cp -a "${x}" "${x}.installer_backup.${installer_backup_suffix}" else # find it relative filename f="${x/*\/etc\/netdata\//}" @@ -573,7 +587,7 @@ do else # edited by user - keep it echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' ${TPUT_RED} has been edited by user${TPUT_RESET}. Keeping it." - run cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}" + run cp -a "${x}" "${x}.installer_backup.${installer_backup_suffix}" fi fi @@ -593,11 +607,11 @@ run make install || exit 1 # ----------------------------------------------------------------------------- progress "Restore user edited netdata configuration files" -for x in $(find "${NETDATA_PREFIX}/etc/netdata/" -name '*.conf' -type f) +for x in $(find -L "${NETDATA_PREFIX}/etc/netdata/" -name '*.conf' -type f) do if [ -f "${x}.installer_backup.${installer_backup_suffix}" ] then - run cp -p "${x}.installer_backup.${installer_backup_suffix}" "${x}" && \ + run cp -a "${x}.installer_backup.${installer_backup_suffix}" "${x}" && \ run rm -f "${x}.installer_backup.${installer_backup_suffix}" fi done @@ -612,42 +626,13 @@ run find ./system/ -type f -a \! -name \*.in -a \! -name Makefile\* -a \! -name # ----------------------------------------------------------------------------- progress "Add user netdata to required user groups" -NETDATA_ADDED_TO_DOCKER=0 -NETDATA_ADDED_TO_NGINX=0 -NETDATA_ADDED_TO_VARNISH=0 -NETDATA_ADDED_TO_HAPROXY=0 -NETDATA_ADDED_TO_ADM=0 -NETDATA_ADDED_TO_NSD=0 -if [ ${UID} -eq 0 ] - then - portable_add_group netdata - portable_add_user netdata - portable_add_user_to_group docker netdata && NETDATA_ADDED_TO_DOCKER=1 - portable_add_user_to_group nginx netdata && NETDATA_ADDED_TO_NGINX=1 - portable_add_user_to_group varnish netdata && NETDATA_ADDED_TO_VARNISH=1 - portable_add_user_to_group haproxy netdata && NETDATA_ADDED_TO_HAPROXY=1 - portable_add_user_to_group adm netdata && NETDATA_ADDED_TO_ADM=1 - portable_add_user_to_group nsd netdata && NETDATA_ADDED_TO_NSD=1 - run_ok -else - run_failed "The installer does not run as root." -fi +add_netdata_user_and_group || run_failed "The installer does not run as root." + # ----------------------------------------------------------------------------- progress "Install logrotate configuration for netdata" -if [ ${UID} -eq 0 ] - then - if [ -d /etc/logrotate.d -a ! -f /etc/logrotate.d/netdata ] - then - run cp system/netdata.logrotate /etc/logrotate.d/netdata - fi - - if [ -f /etc/logrotate.d/netdata ] - then - run chmod 644 /etc/logrotate.d/netdata - fi -fi +install_netdata_logrotate # ----------------------------------------------------------------------------- @@ -659,51 +644,41 @@ progress "Read installation options from netdata.conf" # function to extract values from the config file config_option() { - local key="${1}" value="${2}" line= + local section="${1}" key="${2}" value="${3}" if [ -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ] then - line="$( grep "^[[:space:]]*${key}[[:space:]]*=[[:space:]]*" "${NETDATA_PREFIX}/etc/netdata/netdata.conf" | head -n 1 )" - [ ! -z "${line}" ] && value="$( echo "${line}" | cut -d '=' -f 2 | sed -e "s/^[[:space:]]\+//g" -e "s/[[:space:]]\+$//g" )" + "${NETDATA_PREFIX}/usr/sbin/netdata" \ + -c "${NETDATA_PREFIX}/etc/netdata/netdata.conf" \ + -W get "${section}" "${key}" "${value}" || \ + echo "${value}" + else + echo "${value}" fi - - echo "${value}" } # the user netdata will run as if [ "${UID}" = "0" ] then - NETDATA_USER="$( config_option "run as user" "netdata" )" + NETDATA_USER="$( config_option "global" "run as user" "netdata" )" else NETDATA_USER="${USER}" fi # the owners of the web files -NETDATA_WEB_USER="$( config_option "web files owner" "${NETDATA_USER}" )" -NETDATA_WEB_GROUP="$( config_option "web files group" "${NETDATA_WEB_USER}" )" - -# debug flags -NETDATA_DEBUG="$( config_option "debug flags" 0 )" +NETDATA_WEB_USER="$( config_option "web" "web files owner" "${NETDATA_USER}" )" +NETDATA_WEB_GROUP="$( config_option "web" "web files group" "${NETDATA_WEB_USER}" )" # port defport=19999 -NETDATA_PORT="$( config_option "default port" ${defport} )" -NETDATA_PORT2="$( config_option "port" ${defport} )" - -if [ "${NETDATA_PORT}" != "${NETDATA_PORT2}" ] -then - if [ "${NETDATA_PORT2}" != "${defport}" ] - then - NETDATA_PORT="${NETDATA_PORT2}" - fi -fi +NETDATA_PORT="$( config_option "web" "default port" ${defport} )" # directories -NETDATA_LIB_DIR="$( config_option "lib directory" "${NETDATA_PREFIX}/var/lib/netdata" )" -NETDATA_CACHE_DIR="$( config_option "cache directory" "${NETDATA_PREFIX}/var/cache/netdata" )" -NETDATA_WEB_DIR="$( config_option "web files directory" "${NETDATA_PREFIX}/usr/share/netdata/web" )" -NETDATA_LOG_DIR="$( config_option "log directory" "${NETDATA_PREFIX}/var/log/netdata" )" -NETDATA_CONF_DIR="$( config_option "config directory" "${NETDATA_PREFIX}/etc/netdata" )" +NETDATA_LIB_DIR="$( config_option "global" "lib directory" "${NETDATA_PREFIX}/var/lib/netdata" )" +NETDATA_CACHE_DIR="$( config_option "global" "cache directory" "${NETDATA_PREFIX}/var/cache/netdata" )" +NETDATA_WEB_DIR="$( config_option "global" "web files directory" "${NETDATA_PREFIX}/usr/share/netdata/web" )" +NETDATA_LOG_DIR="$( config_option "global" "log directory" "${NETDATA_PREFIX}/var/log/netdata" )" +NETDATA_CONF_DIR="$( config_option "global" "config directory" "${NETDATA_PREFIX}/etc/netdata" )" NETDATA_RUN_DIR="${NETDATA_PREFIX}/var/run" @@ -824,139 +799,14 @@ fi # ----------------------------------------------------------------------------- progress "Install netdata at system init" -installed_init_d=0 -install_non_systemd_init() { - [ "${UID}" != 0 ] && return 1 - - local key="unknown" - if [ -f /etc/os-release ] - then - source /etc/os-release || return 1 - key="${ID}-${VERSION_ID}" - - elif [ -f /etc/centos-release ] - then - key=$(&2 "Installing systemd service..." - run cp system/netdata.service /etc/systemd/system/netdata.service && \ - run systemctl daemon-reload && \ - run systemctl enable netdata - fi - else - install_non_systemd_init - fi -fi +NETDATA_START_CMD="${NETDATA_PREFIX}/usr/sbin/netdata" +install_netdata_service || run_failed "Cannot install netdata init service." # ----------------------------------------------------------------------------- # check if we can re-start netdata started=0 - -isnetdata() { - if [ -d /proc/self ] - then - [ -z "$1" -o ! -f "/proc/$1/stat" ] && return 1 - [ "$(cat "/proc/$1/stat" | cut -d '(' -f 2 | cut -d ')' -f 1)" = "netdata" ] && return 0 - return 1 - fi - return 0 -} - -stop_netdata_on_pid() { - local pid="${1}" ret=0 count=0 - - isnetdata ${pid} || return 0 - - printf >&2 "Stopping netdata on pid ${pid} ..." - while [ ! -z "$pid" -a ${ret} -eq 0 ] - do - if [ ${count} -gt 45 ] - then - echo >&2 "Cannot stop the running netdata on pid ${pid}." - return 1 - fi - - count=$(( count + 1 )) - - run kill ${pid} 2>/dev/null - ret=$? - - test ${ret} -eq 0 && printf >&2 "." && sleep 2 - done - - echo >&2 - if [ ${ret} -eq 0 ] - then - echo >&2 "SORRY! CANNOT STOP netdata ON PID ${pid} !" - return 1 - fi - - echo >&2 "netdata on pid ${pid} stopped." - return 0 -} - -stop_all_netdata() { - local p myns ns - - myns="$(readlink /proc/self/ns/pid 2>/dev/null)" - - # echo >&2 "Stopping a (possibly) running netdata (namespace '${myns}')..." - - for p in $(cat "${NETDATA_RUN_DIR}/netdata.pid" 2>/dev/null) \ - $(cat /var/run/netdata.pid 2>/dev/null) \ - $(cat /var/run/netdata/netdata.pid 2>/dev/null) \ - $(pidof netdata 2>/dev/null) - do - ns="$(readlink /proc/${p}/ns/pid 2>/dev/null)" - - if [ -z "${myns}" -o -z "${ns}" -o "${myns}" = "${ns}" ] - then - stop_netdata_on_pid ${p} - fi - done -} - if [ ${DONOTSTART} -eq 1 ] then if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ] @@ -971,40 +821,19 @@ if [ ${DONOTSTART} -eq 1 ] fi else - - progress "Start netdata" - - if [ "${UID}" -eq 0 ] + restart_netdata ${NETDATA_PREFIX}/usr/sbin/netdata "${@}" + if [ $? -ne 0 ] then - service netdata stop - stop_all_netdata - service netdata restart && started=1 - if [ ${started} -eq 0 ] - then - service netdata start && started=1 - fi - fi - - if [ ${started} -eq 0 ] - then - # still not started... - - stop_all_netdata - - echo >&2 "Starting netdata..." - run ${NETDATA_PREFIX}/usr/sbin/netdata -P ${NETDATA_RUN_DIR}/netdata.pid "${@}" - if [ $? -ne 0 ] - then - echo >&2 - echo >&2 "SORRY! FAILED TO START NETDATA!" - exit 1 - else - echo >&2 "OK. NetData Started!" - fi - echo >&2 + echo >&2 "SORRY! FAILED TO START NETDATA!" + echo >&2 + exit 1 fi + started=1 + echo >&2 "OK. NetData Started!" + echo >&2 + # ----------------------------------------------------------------------------- # save a config file, if it is not already there @@ -1289,6 +1118,23 @@ if [ $? -eq 0 -a "${NETDATA_ADDED_TO_NSD}" = "1" ] echo " gpasswd -d netdata nsd" fi +getent group proxy > /dev/null +if [ $? -eq 0 -a "${NETDATA_ADDED_TO_PROXY}" = "1" ] + then + echo + echo "You may also want to remove the netdata user from the proxy group" + echo "by running:" + echo " gpasswd -d netdata proxy" +fi + +getent group squid > /dev/null +if [ $? -eq 0 -a "${NETDATA_ADDED_TO_SQUID}" = "1" ] + then + echo + echo "You may also want to remove the netdata user from the squid group" + echo "by running:" + echo " gpasswd -d netdata squid" +fi UNINSTALL chmod 750 netdata-uninstaller.sh @@ -1303,13 +1149,13 @@ so you can access it with: ${TPUT_CYAN}${TPUT_BOLD}http://this.machine.ip:${NETDATA_PORT}/${TPUT_RESET} -To stop netdata, just kill it, with: +To stop netdata run: - ${TPUT_YELLOW}${TPUT_BOLD}killall netdata${TPUT_RESET} + ${TPUT_YELLOW}${TPUT_BOLD}${NETDATA_STOP_CMD}${TPUT_RESET} -To start it, just run it: +To start netdata run: - ${TPUT_YELLOW}${TPUT_BOLD}${NETDATA_PREFIX}/usr/sbin/netdata${TPUT_RESET} + ${TPUT_YELLOW}${TPUT_BOLD}${NETDATA_START_CMD}${TPUT_RESET} END @@ -1327,6 +1173,8 @@ export PATH="\${PATH}:${PATH}" export CFLAGS="${CFLAGS}" export NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS}" +# make sure we have a UID +[ -z "\${UID}" ] && UID="\$(id -u)" INSTALL_UID="${UID}" if [ "\${INSTALL_UID}" != "\${UID}" ] then @@ -1384,10 +1232,7 @@ failed() { } get_latest_commit_id() { - git log -1 2>&3 |\\ - grep ^commit 2>&3 |\\ - head -n 1 2>&3 |\\ - cut -d ' ' -f 2 2>&3 + git rev-parse HEAD 2>&3 } update() { @@ -1417,7 +1262,7 @@ update() { emptyline info "Re-installing netdata..." - ${REINSTALL_COMMAND// --dont-wait/} --dont-wait >&3 2>&3 || failed "FAILED TO COMPILE/INSTALL NETDATA" + ${REINSTALL_COMMAND} --dont-wait >&3 2>&3 || failed "FAILED TO COMPILE/INSTALL NETDATA" [ ! -z "\${tmp}" ] && rm "\${tmp}" && tmp= return 0 @@ -1433,24 +1278,47 @@ REINSTALL echo >&2 "Update script generated : ${TPUT_GREEN}${TPUT_BOLD}./netdata-updater.sh${TPUT_RESET}" echo >&2 echo >&2 "${TPUT_DIM}${TPUT_BOLD}netdata-updater.sh${TPUT_RESET}${TPUT_DIM} can work from cron. It will trigger an email from cron" - echo >&2 "only if it fails (it does not print anything if it can update netdata).${TPUT_RESET}" + echo >&2 "only if it fails (it does not print anything when it can update netdata).${TPUT_RESET}" if [ "${UID}" -eq "0" ] then - if [ -d "/etc/cron.daily" -a ! -f "/etc/cron.daily/netdata-updater.sh" ] + crondir= + [ -d "/etc/periodic/daily" ] && crondir="/etc/periodic/daily" + [ -d "/etc/cron.daily" ] && crondir="/etc/cron.daily" + + if [ ! -z "${crondir}" ] + then + if [ -f "${crondir}/netdata-updater.sh" -a ! -f "${crondir}/netdata-updater" ] then - echo >&2 "${TPUT_DIM}Run this to automatically check and install netdata updates once per day:${TPUT_RESET}" - echo >&2 - echo >&2 "${TPUT_YELLOW}${TPUT_BOLD}ln -s ${PWD}/netdata-updater.sh /etc/cron.daily/netdata-updater.sh${TPUT_RESET}" - elif [ -d "/etc/periodic/daily" -a ! -f "/etc/periodic/daily/netdata-updater" ] + # remove .sh from the filename under cron + progress "Fixing netdata-updater filename at cron" + mv -f "${crondir}/netdata-updater.sh" "${crondir}/netdata-updater" + fi + + if [ ! -f "${crondir}/netdata-updater" ] then - echo >&2 "${TPUT_DIM}Run this to automatically check and install netdata updates once per day:${TPUT_RESET}" - echo >&2 - echo >&2 "${TPUT_YELLOW}${TPUT_BOLD}ln -s ${PWD}/netdata-updater.sh /etc/periodic/daily/netdata-updater${TPUT_RESET}" + if [ "${AUTOUPDATE}" = "1" ] + then + progress "Installing netdata-updater at cron" + run ln -s "${PWD}/netdata-updater.sh" "${crondir}/netdata-updater" + else + echo >&2 "${TPUT_DIM}Run this to automatically check and install netdata updates once per day:${TPUT_RESET}" + echo >&2 + echo >&2 "${TPUT_YELLOW}${TPUT_BOLD}sudo ln -s ${PWD}/netdata-updater.sh ${crondir}/netdata-updater${TPUT_RESET}" + fi + else + progress "Refreshing netdata-updater at cron" + run rm "${crondir}/netdata-updater" + run ln -s "${PWD}/netdata-updater.sh" "${crondir}/netdata-updater" + fi + else + [ "${AUTOUPDATE}" = "1" ] && echo >&2 "Cannot figure out the cron directory to install netdata-updater." fi + else + [ "${AUTOUPDATE}" = "1" ] && echo >&2 "You need to run the installer as root for auto-updating via cron." fi -elif [ -f "netdata-updater.sh" ] - then - rm "netdata-updater.sh" +else + [ -f "netdata-updater.sh" ] && rm "netdata-updater.sh" + [ "${AUTOUPDATE}" = "1" ] && echo >&2 "Your installation method does not support daily auto-updating via cron." fi # ----------------------------------------------------------------------------- diff --git a/netdata.spec b/netdata.spec index 6a3d63f87..3753e27ad 100644 --- a/netdata.spec +++ b/netdata.spec @@ -27,7 +27,8 @@ BuildRequires: systemd-rpm-macros \ %global netdata_init_preun %service_del_preun netdata.service %global netdata_init_postun %service_del_postun netdata.service %else -%global netdata_initd_buildrequires %{nil} +%global netdata_initd_buildrequires \ +BuildRequires: systemd %global netdata_initd_requires \ Requires(preun): systemd-units \ Requires(postun): systemd-units \ @@ -76,11 +77,11 @@ Recommends: python2-psycopg2 \ Summary: Real-time performance monitoring, done right Name: netdata -Version: 1.6.0 +Version: 1.7.0 Release: 1%{?dist} License: GPLv3+ Group: Applications/System -Source0: https://firehol.org/download/netdata/releases/v1.6.0/%{name}-1.6.0.tar.xz +Source0: https://github.com/firehol/%{name}/releases/download/v1.7.0/%{name}-1.7.0.tar.xz URL: http://my-netdata.io BuildRequires: pkgconfig BuildRequires: xz @@ -116,7 +117,7 @@ so that you can get insights of what is happening now and what just happened, on your systems and applications. %prep -%setup -q -n netdata-1.6.0 +%setup -q -n netdata-1.7.0 %build %configure \ @@ -176,6 +177,7 @@ rm -rf "${RPM_BUILD_ROOT}" %config(noreplace) %{_sysconfdir}/%{name}/health.d/*.conf #%%config(noreplace) %{_sysconfdir}/%{name}/node.d/*.conf %config(noreplace) %{_sysconfdir}/%{name}/python.d/*.conf +%config(noreplace) %{_sysconfdir}/%{name}/statsd.d/*.conf %config(noreplace) %{_sysconfdir}/logrotate.d/%{name} # To be eventually moved to %%_defaultdocdir @@ -205,6 +207,21 @@ rm -rf "${RPM_BUILD_ROOT}" %{_datadir}/%{name}/web %changelog +* Mon Jul 16 2017 Costa Tsaousis - 1.7.0-1 +- netdata is now a fully featured statsd server +- new installation options +- metrics streaming and replication improvements +- backends improvements - prometheus support rewritten +- netdata now monitors ZFS (on Linux and FreeBSD) +- netdata now monitors ElasticSearch +- netdata now monitors RabbitMQ +- netdata now monitors Go applications (via expvar) +- netdata now monitors ipfw (on FreeBSD 11) +- netdata now monitors samba +- netdata now monitors squid logs +- netdata dashboard loading times have been improved significantly +- netdata alarms now support custom hooks +- dozens more improvements and bug fixes * Mon Mar 20 2017 Costa Tsaousis - 1.6.0-1 - central netdata - monitoring ephemeral nodes diff --git a/netdata.spec.in b/netdata.spec.in index 685d6e0e7..6ba791232 100644 --- a/netdata.spec.in +++ b/netdata.spec.in @@ -27,7 +27,8 @@ BuildRequires: systemd-rpm-macros \ %global netdata_init_preun %service_del_preun netdata.service %global netdata_init_postun %service_del_postun netdata.service %else -%global netdata_initd_buildrequires %{nil} +%global netdata_initd_buildrequires \ +BuildRequires: systemd %global netdata_initd_requires \ Requires(preun): systemd-units \ Requires(postun): systemd-units \ @@ -80,7 +81,7 @@ Version: @PACKAGE_RPM_VERSION@ Release: 1%{?dist} License: GPLv3+ Group: Applications/System -Source0: https://firehol.org/download/netdata/releases/v@PACKAGE_VERSION@/%{name}-@PACKAGE_VERSION@.tar.xz +Source0: https://github.com/firehol/%{name}/releases/download/v@PACKAGE_VERSION@/%{name}-@PACKAGE_VERSION@.tar.xz URL: http://my-netdata.io BuildRequires: pkgconfig BuildRequires: xz @@ -176,6 +177,7 @@ rm -rf "${RPM_BUILD_ROOT}" %config(noreplace) %{_sysconfdir}/%{name}/health.d/*.conf #%%config(noreplace) %{_sysconfdir}/%{name}/node.d/*.conf %config(noreplace) %{_sysconfdir}/%{name}/python.d/*.conf +%config(noreplace) %{_sysconfdir}/%{name}/statsd.d/*.conf %config(noreplace) %{_sysconfdir}/logrotate.d/%{name} # To be eventually moved to %%_defaultdocdir @@ -205,6 +207,21 @@ rm -rf "${RPM_BUILD_ROOT}" %{_datadir}/%{name}/web %changelog +* Mon Jul 16 2017 Costa Tsaousis - 1.7.0-1 +- netdata is now a fully featured statsd server +- new installation options +- metrics streaming and replication improvements +- backends improvements - prometheus support rewritten +- netdata now monitors ZFS (on Linux and FreeBSD) +- netdata now monitors ElasticSearch +- netdata now monitors RabbitMQ +- netdata now monitors Go applications (via expvar) +- netdata now monitors ipfw (on FreeBSD 11) +- netdata now monitors samba +- netdata now monitors squid logs +- netdata dashboard loading times have been improved significantly +- netdata alarms now support custom hooks +- dozens more improvements and bug fixes * Mon Mar 20 2017 Costa Tsaousis - 1.6.0-1 - central netdata - monitoring ephemeral nodes diff --git a/node.d/Makefile.am b/node.d/Makefile.am index c1caa4f0e..28008aeb7 100644 --- a/node.d/Makefile.am +++ b/node.d/Makefile.am @@ -3,6 +3,7 @@ MAINTAINERCLEANFILES= $(srcdir)/Makefile.in dist_node_DATA = \ README.md \ named.node.js \ + fronius.node.js \ sma_webbox.node.js \ snmp.node.js \ $(NULL) diff --git a/node.d/Makefile.in b/node.d/Makefile.in index 2af9a4a65..35024cb12 100644 --- a/node.d/Makefile.in +++ b/node.d/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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,6 +15,51 @@ @SET_MAKE@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -35,9 +79,9 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = node.d -DIST_COMMON = $(dist_node_DATA) $(dist_nodemodules_DATA) \ - $(dist_nodemodulesber_DATA) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(dist_node_DATA) $(dist_nodemodules_DATA) \ + $(dist_nodemodulesber_DATA) 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_lto.m4 \ @@ -53,8 +97,25 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +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 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -86,9 +147,11 @@ am__installdirs = "$(DESTDIR)$(nodedir)" "$(DESTDIR)$(nodemodulesdir)" \ "$(DESTDIR)$(nodemodulesberdir)" DATA = $(dist_node_DATA) $(dist_nodemodules_DATA) \ $(dist_nodemodulesber_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -234,6 +297,7 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in dist_node_DATA = \ README.md \ named.node.js \ + fronius.node.js \ sma_webbox.node.js \ snmp.node.js \ $(NULL) @@ -291,8 +355,11 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_nodeDATA: $(dist_node_DATA) @$(NORMAL_INSTALL) - test -z "$(nodedir)" || $(MKDIR_P) "$(DESTDIR)$(nodedir)" @list='$(dist_node_DATA)'; test -n "$(nodedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(nodedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(nodedir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -309,8 +376,11 @@ uninstall-dist_nodeDATA: dir='$(DESTDIR)$(nodedir)'; $(am__uninstall_files_from_dir) install-dist_nodemodulesDATA: $(dist_nodemodules_DATA) @$(NORMAL_INSTALL) - test -z "$(nodemodulesdir)" || $(MKDIR_P) "$(DESTDIR)$(nodemodulesdir)" @list='$(dist_nodemodules_DATA)'; test -n "$(nodemodulesdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(nodemodulesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(nodemodulesdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -327,8 +397,11 @@ uninstall-dist_nodemodulesDATA: dir='$(DESTDIR)$(nodemodulesdir)'; $(am__uninstall_files_from_dir) install-dist_nodemodulesberDATA: $(dist_nodemodulesber_DATA) @$(NORMAL_INSTALL) - test -z "$(nodemodulesberdir)" || $(MKDIR_P) "$(DESTDIR)$(nodemodulesberdir)" @list='$(dist_nodemodulesber_DATA)'; test -n "$(nodemodulesberdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(nodemodulesberdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(nodemodulesberdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -343,11 +416,11 @@ uninstall-dist_nodemodulesberDATA: @list='$(dist_nodemodulesber_DATA)'; test -n "$(nodemodulesberdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(nodemodulesberdir)'; $(am__uninstall_files_from_dir) -tags: TAGS -TAGS: +tags TAGS: + +ctags CTAGS: -ctags: CTAGS -CTAGS: +cscope cscopelist: distdir: $(DISTFILES) @@ -488,17 +561,18 @@ uninstall-am: uninstall-dist_nodeDATA uninstall-dist_nodemodulesDATA \ .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-dist_nodeDATA install-dist_nodemodulesDATA \ - install-dist_nodemodulesberDATA 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-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ - pdf-am ps ps-am uninstall uninstall-am uninstall-dist_nodeDATA \ +.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ + ctags-am distclean distclean-generic distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dist_nodeDATA \ + install-dist_nodemodulesDATA install-dist_nodemodulesberDATA \ + 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-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags-am \ + uninstall uninstall-am uninstall-dist_nodeDATA \ uninstall-dist_nodemodulesDATA \ uninstall-dist_nodemodulesberDATA diff --git a/node.d/README.md b/node.d/README.md index e69de29bb..3c2977905 100644 --- a/node.d/README.md +++ b/node.d/README.md @@ -0,0 +1,63 @@ +# Disclaimer + +Module configurations are written in JSON and **node.js is required**. + +to be edited. + +--- + +The following node.d modules are supported: + +# fronius + +This module collects metrics from the configured solar power installation from Fronius Symo. +See `netdata/conf.d/node.d/fronius.conf.md` for more details. + +**Requirements** + * Configuration file `fronius.conf` in the node.d netdata config dir (default: `/etc/netdata/node.d/fronius.conf`) + * Fronius Symo with network access (http) + +It produces per server: + +1. **Power** + * Current power input from the grid (positive values), output to the grid (negative values), in W + * Current power input from the solar panels, in W + * Current power stored in the accumulator (if present), in W (in theory, untested) + +2. **Consumption** + * Local consumption in W + +3. **Autonomy** + * Relative autonomy in %. 100 % autonomy means that the solar panels are delivering more power than it is needed by local consumption. + * Relative self consumption in %. The lower the better + +4. **Energy** + * The energy produced during the current day, in kWh + * The energy produced during the current year, in kWh + +5. **Inverter** + * The current power output from the connected inverters, in W, one dimension per inverter. At least one is always present. + + +### configuration + +Sample: + +```json +{ + "enable_autodetect": false, + "update_every": 5, + "servers": [ + { + "name": "Symo", + "hostname": "symo.ip.or.dns", + "update_every": 5, + "api_path": "/solar_api/v1/GetPowerFlowRealtimeData.fcgi" + } + ] +} +``` + +If no configuration is given, the module will be disabled. Each `update_every` is optional, the default is `5`. + +--- diff --git a/node.d/fronius.node.js b/node.d/fronius.node.js new file mode 100644 index 000000000..f771f6c3d --- /dev/null +++ b/node.d/fronius.node.js @@ -0,0 +1,317 @@ +'use strict'; + +// This program will connect to one or more Fronius Symo Inverters. +// to get the Solar Power Generated (current, today). + +// example configuration in netdata/conf.d/node.d/fronius.conf.md + +var url = require('url'); +var http = require('http'); +var netdata = require('netdata'); + +netdata.debug('loaded ' + __filename + ' plugin'); + +var fronius = { + name: "Fronius", + enable_autodetect: false, + update_every: 5, + base_priority: 60000, + charts: {}, + + powerGridId: "p_grid", + powerPvId: "p_pv", + powerAccuId: "p_akku", // not my typo! Using the ID from the AP + consumptionLoadId: "p_load", + autonomyId: "rel_autonomy", + consumptionSelfId: "rel_selfconsumption", + energyTodayId: "e_day", + energyYearId: "e_year", + + createBasicDimension: function (id, name, divisor) { + return { + id: id, // the unique id of the dimension + name: name, // the name of the dimension + algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: divisor, // the divisor + hidden: false // is hidden (boolean) + }; + }, + + // Gets the site power chart. Will be created if not existing. + getSitePowerChart: function (service, id) { + var chart = fronius.charts[id]; + if (fronius.isDefined(chart)) return chart; + + var dim = {}; + dim[fronius.powerGridId] = this.createBasicDimension(fronius.powerGridId, "Grid", 1); + dim[fronius.powerPvId] = this.createBasicDimension(fronius.powerPvId, "Photovoltaics", 1); + dim[fronius.powerAccuId] = this.createBasicDimension(fronius.powerAccuId, "Accumulator", 1); + + chart = { + id: id, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Current Site Power', // the title of the chart + units: 'W', // the units of the chart dimensions + family: 'power', // the family of the chart + context: 'fronius.power', // the context of the chart + type: netdata.chartTypes.area, // the type of the chart + priority: fronius.base_priority + 1, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(id, chart); + fronius.charts[id] = chart; + + return chart; + }, + + // Gets the site consumption chart. Will be created if not existing. + getSiteConsumptionChart: function (service, id) { + var chart = fronius.charts[id]; + if (fronius.isDefined(chart)) return chart; + var dim = {}; + dim[fronius.consumptionLoadId] = this.createBasicDimension(fronius.consumptionLoadId, "Load", 1); + + chart = { + id: id, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Current Load', // the title of the chart + units: 'W', // the units of the chart dimensions + family: 'consumption', // the family of the chart + context: 'fronius.consumption', // the context of the chart + type: netdata.chartTypes.area, // the type of the chart + priority: fronius.base_priority + 2, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(id, chart); + fronius.charts[id] = chart; + + return chart; + }, + + // Gets the site consumption chart. Will be created if not existing. + getSiteAutonomyChart: function (service, id) { + var chart = fronius.charts[id]; + if (fronius.isDefined(chart)) return chart; + var dim = {}; + dim[fronius.autonomyId] = this.createBasicDimension(fronius.autonomyId, "Autonomy", 1); + dim[fronius.consumptionSelfId] = this.createBasicDimension(fronius.consumptionSelfId, "Self Consumption", 1); + + chart = { + id: id, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Current Autonomy', // the title of the chart + units: '%', // the units of the chart dimensions + family: 'autonomy', // the family of the chart + context: 'fronius.autonomy', // the context of the chart + type: netdata.chartTypes.area, // the type of the chart + priority: fronius.base_priority + 3, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(id, chart); + fronius.charts[id] = chart; + + return chart; + }, + + // Gets the site energy chart for today. Will be created if not existing. + getSiteEnergyTodayChart: function (service, chartId) { + var chart = fronius.charts[chartId]; + if (fronius.isDefined(chart)) return chart; + var dim = {}; + dim[fronius.energyTodayId] = this.createBasicDimension(fronius.energyTodayId, "Today", 1000); + chart = { + id: chartId, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Energy production for today', // the title of the chart + units: 'kWh', // the units of the chart dimensions + family: 'energy', // the family of the chart + context: 'fronius.energy.today', // the context of the chart + type: netdata.chartTypes.area, // the type of the chart + priority: fronius.base_priority + 4, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(chartId, chart); + fronius.charts[chartId] = chart; + + return chart; + }, + + // Gets the site energy chart for today. Will be created if not existing. + getSiteEnergyYearChart: function (service, chartId) { + var chart = fronius.charts[chartId]; + if (fronius.isDefined(chart)) return chart; + var dim = {}; + dim[fronius.energyYearId] = this.createBasicDimension(fronius.energyYearId, "Year", 1000); + chart = { + id: chartId, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Energy production for this year', // the title of the chart + units: 'kWh', // the units of the chart dimensions + family: 'energy', // the family of the chart + context: 'fronius.energy.year', // the context of the chart + type: netdata.chartTypes.area, // the type of the chart + priority: fronius.base_priority + 5, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(chartId, chart); + fronius.charts[chartId] = chart; + + return chart; + }, + + // Gets the inverter power chart. Will be created if not existing. + // Needs the array of inverters in order to create a chart with all inverters as dimensions + getInverterPowerChart: function (service, chartId, inverters) { + + var chart = fronius.charts[chartId]; + if (fronius.isDefined(chart)) return chart; + + var dim = {}; + + var inverterCount = Object.keys(inverters).length; + var inverter = inverters[inverterCount.toString()]; + var i = 1; + for (i; i <= inverterCount; i++) { + if (fronius.isUndefined(inverter)) { + netdata.error("Expected an Inverter with a numerical name! " + + "Have a look at your JSON output to verify."); + continue; + } + dim[i.toString()] = this.createBasicDimension("inverter_" + i, "Inverter " + i, 1); + } + + chart = { + id: chartId, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Current Inverter Output', // the title of the chart + units: 'W', // the units of the chart dimensions + family: 'inverters', // the family of the chart + context: 'fronius.inverter.output', // the context of the chart + type: netdata.chartTypes.stacked, // the type of the chart + priority: fronius.base_priority + 6, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(chartId, chart); + fronius.charts[chartId] = chart; + + return chart; + }, + + processResponse: function (service, content) { + if (content === null) return; + var json = JSON.parse(content); + if (!fronius.isResponseValid(json)) return; + + // add the service + service.commit(); + + var site = json.Body.Data.Site; + + // Site Current Power Chart + service.begin(fronius.getSitePowerChart(service, 'fronius_' + service.name + '.power')); + service.set(fronius.powerGridId, Math.round(site.P_Grid)); + service.set(fronius.powerPvId, Math.round(site.P_PV)); + service.set(fronius.powerAccuId, Math.round(site.P_Akku)); + service.end(); + + // Site Consumption Chart + service.begin(fronius.getSiteConsumptionChart(service, 'fronius_' + service.name + '.consumption')); + service.set(fronius.consumptionLoadId, Math.round(Math.abs(site.P_Load))); + service.end(); + + // Site Autonomy Chart + service.begin(fronius.getSiteAutonomyChart(service, 'fronius_' + service.name + '.autonomy')); + service.set(fronius.autonomyId, Math.round(site.rel_Autonomy)); + var selfConsumption = site.rel_SelfConsumption; + service.set(fronius.consumptionSelfId, Math.round(selfConsumption === null ? 100 : selfConsumption)); + service.end(); + + // Site Energy Today Chart + service.begin(fronius.getSiteEnergyTodayChart(service, 'fronius_' + service.name + '.energy.today')); + service.set(fronius.energyTodayId, Math.round(site.E_Day)); + service.end(); + + // Site Energy Year Chart + service.begin(fronius.getSiteEnergyYearChart(service, 'fronius_' + service.name + '.energy.year')); + service.set(fronius.energyYearId, Math.round(site.E_Year)); + service.end(); + + // Inverters + var inverters = json.Body.Data.Inverters; + var inverterCount = Object.keys(inverters).length + 1; + while (inverterCount--) { + var inverter = inverters[inverterCount]; + if (fronius.isUndefined(inverter)) continue; + service.begin(fronius.getInverterPowerChart(service, 'fronius_' + service.name + '.inverters.output', inverters)); + service.set(inverterCount.toString(), Math.round(inverter.P)); + service.end(); + } + }, + + // some basic validation + isResponseValid: function (json) { + if (fronius.isUndefined(json.Body)) return false; + if (fronius.isUndefined(json.Body.Data)) return false; + if (fronius.isUndefined(json.Body.Data.Site)) return false; + return fronius.isDefined(json.Body.Data.Inverters); + }, + + // module.serviceExecute() + // this function is called only from this module + // its purpose is to prepare the request and call + // netdata.serviceExecute() + serviceExecute: function (name, uri, update_every) { + netdata.debug(this.name + ': ' + name + ': url: ' + uri + ', update_every: ' + update_every); + + var service = netdata.service({ + name: name, + request: netdata.requestFromURL('http://' + uri), + update_every: update_every, + module: this + }); + service.execute(this.processResponse); + }, + + + configure: function (config) { + if (fronius.isUndefined(config.servers)) return 0; + var added = 0; + var len = config.servers.length; + while (len--) { + var server = config.servers[len]; + if (fronius.isUndefined(server.update_every)) server.update_every = this.update_every; + + var url = server.hostname + server.api_path; + this.serviceExecute(server.name, url, server.update_every); + added++; + } + return added; + }, + + // module.update() + // this is called repeatedly to collect data, by calling + // netdata.serviceExecute() + update: function (service, callback) { + service.execute(function (serv, data) { + service.module.processResponse(serv, data); + callback(); + }); + }, + + isUndefined: function (value) { + return typeof value === 'undefined'; + }, + + isDefined: function (value) { + return typeof value !== 'undefined'; + } +}; + +module.exports = fronius; diff --git a/node.d/node_modules/net-snmp.js b/node.d/node_modules/net-snmp.js index 6fbd4e721..de5926104 100644 --- a/node.d/node_modules/net-snmp.js +++ b/node.d/node_modules/net-snmp.js @@ -12,7 +12,7 @@ var util = require ("util"); function _expandConstantObject (object) { var keys = []; - for (key in object) + for (var key in object) keys.push (key); for (var i = 0; i < keys.length; i++) object[object[keys[i]]] = parseInt (keys[i]); @@ -133,12 +133,9 @@ util.inherits (RequestTimedOutError, Error); **/ function isVarbindError (varbind) { - if (varbind.type == ObjectType.NoSuchObject - || varbind.type == ObjectType.NoSuchInstance - || varbind.type == ObjectType.EndOfMibView) - return true; - else - return false; + return !!(varbind.type == ObjectType.NoSuchObject + || varbind.type == ObjectType.NoSuchInstance + || varbind.type == ObjectType.EndOfMibView); } function varbindError (varbind) { @@ -216,6 +213,8 @@ function readInt (buffer) { function readUint (buffer, isSigned) { buffer.readByte (); var length = buffer.readByte (); + var value = 0; + var signedBitSet = false; if (length > 5) { throw new RangeError ("Integer too long '" + length + "'"); @@ -225,8 +224,6 @@ function readUint (buffer, isSigned) { length = 4; } - value = 0, signedBitSet = false; - for (var i = 0; i < length; i++) { value *= 256; value += buffer.readByte (); @@ -246,10 +243,6 @@ function readUint (buffer, isSigned) { function readUint64 (buffer) { var value = buffer.readString (ObjectType.Counter64, true); - if (value.length > 8) - throw new RequestInvalidError ("64 bit unsigned integer too long '" - + value.length + "'") - return value; } @@ -327,9 +320,6 @@ function writeUint (buffer, type, value) { } function writeUint64 (buffer, value) { - if (value.length > 8) - throw new RequestInvalidError ("64 bit unsigned integer too long '" - + value.length + "'") buffer.writeBuffer (value, ObjectType.Counter64); } @@ -381,7 +371,7 @@ function writeVarbinds (buffer, varbinds) { } buffer.endSequence (); - }; + } buffer.endSequence (); } @@ -549,7 +539,7 @@ var ResponseMessage = function (buffer) { throw new ResponseInvalidError ("Unknown PDU type '" + type + "' in response"); } -} +}; /***************************************************************************** ** Session class definition @@ -599,7 +589,7 @@ var Session = function (target, community, options) { this.dgram.on ("error", me.onError.bind (me)); if (this.sourceAddress || this.sourcePort) - req.dgram.bind (this.sourcePort, this.sourceAddress); + this.dgram.bind (this.sourcePort, this.sourceAddress); }; util.inherits (Session, events.EventEmitter); @@ -607,15 +597,16 @@ util.inherits (Session, events.EventEmitter); Session.prototype.close = function () { this.dgram.close (); return this; -} +}; Session.prototype.cancelRequests = function (error) { + var id; for (id in this.reqs) { var req = this.reqs[id]; this.unregisterRequest (req.id); req.responseCb (error); } -} +}; function _generateId () { return Math.floor (Math.random () + Math.random () * 10000000) @@ -645,7 +636,7 @@ Session.prototype.get = function (oids, responseCb) { req.responseCb (null, varbinds); } - }; + } var pduVarbinds = []; @@ -747,7 +738,7 @@ Session.prototype.getBulk = function () { } req.responseCb (null, varbinds); - }; + } var pduVarbinds = []; @@ -796,7 +787,7 @@ Session.prototype.getNext = function (oids, responseCb) { req.responseCb (null, varbinds); } - }; + } var pduVarbinds = []; @@ -813,7 +804,7 @@ Session.prototype.getNext = function (oids, responseCb) { }; Session.prototype.inform = function () { - var typeOrOid = arguments[0];; + var typeOrOid = arguments[0]; var varbinds, options = {}, responseCb; /** @@ -865,7 +856,7 @@ Session.prototype.inform = function () { req.responseCb (null, varbinds); } - }; + } if (typeof typeOrOid != "string") typeOrOid = "1.3.6.1.6.3.1.1.5." + (typeOrOid + 1); @@ -1029,7 +1020,7 @@ Session.prototype.set = function (varbinds, responseCb) { req.responseCb (null, varbinds); } - }; + } var pduVarbinds = []; @@ -1049,7 +1040,7 @@ Session.prototype.set = function (varbinds, responseCb) { Session.prototype.simpleGet = function (pduClass, feedCb, varbinds, responseCb, options) { - var req = {} + var req = {}; try { var id = _generateId (); @@ -1116,7 +1107,7 @@ Session.prototype.subtree = function () { this.walk (oid, maxRepetitions, subtreeCb.bind (me, req), doneCb); return this; -} +}; function tableColumnsResponseCb (req, error) { if (error) { @@ -1143,7 +1134,7 @@ function tableColumnsFeedCb (req, varbinds) { return true; } - var oid = varbinds[i].oid.replace (req.rowOid, "") + var oid = varbinds[i].oid.replace (req.rowOid, ""); if (oid && oid != varbinds[i].oid) { var match = oid.match (/^(\d+)\.(.+)$/); if (match && match[1] > 0) { @@ -1187,7 +1178,7 @@ Session.prototype.tableColumns = function () { } return this; -} +}; function tableResponseCb (req, error) { if (error) @@ -1205,7 +1196,7 @@ function tableFeedCb (req, varbinds) { return true; } - var oid = varbinds[i].oid.replace (req.rowOid, "") + var oid = varbinds[i].oid.replace (req.rowOid, ""); if (oid && oid != varbinds[i].oid) { var match = oid.match (/^(\d+)\.(.+)$/); if (match && match[1] > 0) { @@ -1243,7 +1234,7 @@ Session.prototype.table = function () { tableResponseCb.bind (me, req)); return this; -} +}; Session.prototype.trap = function () { var req = {}; @@ -1430,7 +1421,7 @@ Session.prototype.walk = function () { this.getNext ([oid], walkCb.bind (me, req)); return this; -} +}; /***************************************************************************** ** Exports @@ -1438,8 +1429,8 @@ Session.prototype.walk = function () { exports.Session = Session; -exports.createSession = function (target, community, version, options) { - return new Session (target, community, version, options); +exports.createSession = function (target, community, options) { + return new Session (target, community, options); }; exports.isVarbindError = isVarbindError; diff --git a/plugins.d/Makefile.in b/plugins.d/Makefile.in index 2a8806cb1..256605f5d 100644 --- a/plugins.d/Makefile.in +++ b/plugins.d/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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. @@ -17,6 +16,51 @@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -36,8 +80,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = plugins.d -DIST_COMMON = $(dist_plugins_DATA) $(dist_plugins_SCRIPTS) \ - $(srcdir)/Makefile.am $(srcdir)/Makefile.in +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(dist_plugins_SCRIPTS) $(dist_plugins_DATA) 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_lto.m4 \ @@ -82,12 +126,31 @@ am__uninstall_files_from_dir = { \ } am__installdirs = "$(DESTDIR)$(pluginsdir)" "$(DESTDIR)$(pluginsdir)" SCRIPTS = $(dist_plugins_SCRIPTS) +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 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac DATA = $(dist_plugins_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -287,8 +350,11 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_pluginsSCRIPTS: $(dist_plugins_SCRIPTS) @$(NORMAL_INSTALL) - test -z "$(pluginsdir)" || $(MKDIR_P) "$(DESTDIR)$(pluginsdir)" @list='$(dist_plugins_SCRIPTS)'; 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 \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ @@ -319,8 +385,11 @@ uninstall-dist_pluginsSCRIPTS: dir='$(DESTDIR)$(pluginsdir)'; $(am__uninstall_files_from_dir) install-dist_pluginsDATA: $(dist_plugins_DATA) @$(NORMAL_INSTALL) - test -z "$(pluginsdir)" || $(MKDIR_P) "$(DESTDIR)$(pluginsdir)" @list='$(dist_plugins_DATA)'; 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 \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -335,11 +404,11 @@ uninstall-dist_pluginsDATA: @list='$(dist_plugins_DATA)'; test -n "$(pluginsdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pluginsdir)'; $(am__uninstall_files_from_dir) -tags: TAGS -TAGS: +tags TAGS: + +ctags CTAGS: -ctags: CTAGS -CTAGS: +cscope cscopelist: distdir: $(DISTFILES) @@ -478,18 +547,18 @@ uninstall-am: uninstall-dist_pluginsDATA uninstall-dist_pluginsSCRIPTS .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-dist_pluginsDATA install-dist_pluginsSCRIPTS \ - 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-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ - uninstall-am uninstall-dist_pluginsDATA \ - uninstall-dist_pluginsSCRIPTS +.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ + ctags-am distclean distclean-generic distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dist_pluginsDATA \ + install-dist_pluginsSCRIPTS 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-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am tags-am uninstall uninstall-am \ + uninstall-dist_pluginsDATA uninstall-dist_pluginsSCRIPTS # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/plugins.d/alarm-notify.sh b/plugins.d/alarm-notify.sh index d0188fe3b..4f619091f 100755 --- a/plugins.d/alarm-notify.sh +++ b/plugins.d/alarm-notify.sh @@ -2,7 +2,7 @@ # netdata # real-time performance and health monitoring, done right! -# (C) 2016 Costa Tsaousis +# (C) 2017 Costa Tsaousis # GPL v3+ # # Script to send alarm notifications for netdata @@ -18,13 +18,14 @@ # - slack.com notifications by @ktsaou # - discordapp.com notifications by @lowfive # - pushover.net notifications by @ktsaou -# - pushbullet.com push notifications by Tiago Peralta @tperalta82 PR #1070 -# - telegram.org notifications by @hashworks PR #1002 -# - twilio.com notifications by Levi Blaney @shadycuz PR #1211 +# - pushbullet.com push notifications by Tiago Peralta @tperalta82 #1070 +# - telegram.org notifications by @hashworks #1002 +# - twilio.com notifications by Levi Blaney @shadycuz #1211 # - kafka notifications by @ktsaou #1342 -# - pagerduty.com notifications by Jim Cooley @jimcooley PR #1373 +# - pagerduty.com notifications by Jim Cooley @jimcooley #1373 # - messagebird.com notifications by @tech_no_logical #1453 # - hipchat notifications by @ktsaou #1561 +# - custom notifications by @ktsaou # ----------------------------------------------------------------------------- # testing notifications @@ -103,6 +104,15 @@ debug() { [ ${debug} -eq 1 ] && log DEBUG "${@}" } + +# ----------------------------------------------------------------------------- +# this is to be overwritten by the config file + +custom_sender() { + info "not sending custom notification for ${status} of '${host}.${chart}.${name}'" +} + + # ----------------------------------------------------------------------------- # check for BASH v4+ (required for associative arrays) @@ -112,11 +122,9 @@ debug() { # ----------------------------------------------------------------------------- # defaults to allow running this script by hand -NETDATA_CONFIG_DIR="${NETDATA_CONFIG_DIR-/etc/netdata}" -NETDATA_CACHE_DIR="${NETDATA_CACHE_DIR-/var/cache/netdata}" +[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata" +[ -z "${NETDATA_CACHE_DIR}" ] && NETDATA_CACHE_DIR="$(dirname "${0}")/../../../../var/cache/netdata" [ -z "${NETDATA_REGISTRY_URL}" ] && NETDATA_REGISTRY_URL="https://registry.my-netdata.io" -[ -z "${NETDATA_HOSTNAME}" ] && NETDATA_HOSTNAME="$(hostname)" -[ -z "${NETDATA_REGISTRY_HOSTNAME}" ] && NETDATA_REGISTRY_HOSTNAME="${NETDATA_HOSTNAME}" # ----------------------------------------------------------------------------- # parse command line parameters @@ -130,8 +138,8 @@ when="${6}" # the timestamp this event occurred name="${7}" # the name of the alarm, as given in netdata health.d entries chart="${8}" # the name of the chart (type.id) family="${9}" # the family of the chart -status="${10}" # the current status : REMOVED, UNITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL -old_status="${11}" # the previous status: REMOVED, UNITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL +status="${10}" # the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL +old_status="${11}" # the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL value="${12}" # the current value of the alarm old_value="${13}" # the previous value of the alarm src="${14}" # the line number and file the alarm has been configured @@ -145,9 +153,8 @@ old_value_string="${20}" # friendly old value (with units) # ----------------------------------------------------------------------------- # find a suitable hostname to use, if netdata did not supply a hostname -[ -z "${host}" ] && host="${NETDATA_HOSTNAME}" -[ -z "${host}" ] && host="${NETDATA_REGISTRY_HOSTNAME}" -[ -z "${host}" ] && host="$(hostname 2>/dev/null)" +this_host=$(hostname -s 2>/dev/null) +[ -z "${host}" ] && host="${this_host}" # ----------------------------------------------------------------------------- # screen statuses we don't need to send a notification @@ -192,6 +199,7 @@ SEND_EMAIL="YES" SEND_PUSHBULLET="YES" SEND_KAFKA="YES" SEND_PD="YES" +SEND_CUSTOM="YES" # slack configs SLACK_WEBHOOK_URL= @@ -245,6 +253,10 @@ KAFKA_SENDER_IP= PD_SERVICE_KEY= declare -A role_recipients_pd=() +# custom configs +DEFAULT_RECIPIENT_CUSTOM= +declare -A role_recipients_custom=() + # email configs DEFAULT_RECIPIENT_EMAIL="root" declare -A role_recipients_email=() @@ -308,6 +320,7 @@ declare -A arr_hipchat=() declare -A arr_telegram=() declare -A arr_pd=() declare -A arr_email=() +declare -A arr_custom=() # netdata may call us with multiple roles, and roles may have multiple but # overlapping recipients - so, here we find the unique recipients. @@ -396,6 +409,15 @@ do do [ "${r}" != "disabled" ] && filter_recipient_by_criticality pd "${r}" && arr_pd[${r/|*/}]="1" done + + # custom + a="${role_recipients_custom[${x}]}" + [ -z "${a}" ] && a="${DEFAULT_RECIPIENT_CUSTOM}" + for r in ${a//,/ } + do + [ "${r}" != "disabled" ] && filter_recipient_by_criticality custom "${r}" && arr_custom[${r/|*/}]="1" + done + done # build the list of slack recipients (channels) @@ -434,6 +456,10 @@ to_telegram="${!arr_telegram[*]}" to_pd="${!arr_pd[*]}" [ -z "${to_pd}" ] && SEND_PD="NO" +# build the list of custom recipients +to_custom="${!arr_custom[*]}" +[ -z "${to_custom}" ] && SEND_CUSTOM="NO" + # build the list of email recipients (email addresses) to_email= for x in "${!arr_email[@]}" @@ -492,7 +518,7 @@ fi if [ \( \ "${SEND_PUSHOVER}" = "YES" \ -o "${SEND_SLACK}" = "YES" \ - -o "${SEND_DISCORD}" = "YES" \ + -o "${SEND_DISCORD}" = "YES" \ -o "${SEND_HIPCHAT}" = "YES" \ -o "${SEND_TWILIO}" = "YES" \ -o "${SEND_MESSAGEBIRD}" = "YES" \ @@ -530,13 +556,14 @@ if [ "${SEND_EMAIL}" != "YES" \ -a "${SEND_PUSHOVER}" != "YES" \ -a "${SEND_TELEGRAM}" != "YES" \ -a "${SEND_SLACK}" != "YES" \ - -a "${SEND_DISCORD}" != "YES" \ + -a "${SEND_DISCORD}" != "YES" \ -a "${SEND_TWILIO}" != "YES" \ -a "${SEND_HIPCHAT}" != "YES" \ -a "${SEND_MESSAGEBIRD}" != "YES" \ -a "${SEND_PUSHBULLET}" != "YES" \ -a "${SEND_KAFKA}" != "YES" \ -a "${SEND_PD}" != "YES" \ + -a "${SEND_CUSTOM}" != "YES" \ ] then fatal "All notification methods are disabled. Not sending notification for host '${host}', chart '${chart}' to '${roles}' for '${name}' = '${value}' for status '${status}'." @@ -939,9 +966,16 @@ send_messagebird() { # telegram sender send_telegram() { - local bottoken="${1}" chatids="${2}" message="${3}" httpcode sent=0 chatid disableNotification="" + local bottoken="${1}" chatids="${2}" message="${3}" httpcode sent=0 chatid emoji disableNotification="" if [ "${status}" = "CLEAR" ]; then disableNotification="--data-urlencode disable_notification=true"; fi + + case "${status}" in + WARNING) emoji="⚠️" ;; + CRITICAL) emoji="🔴" ;; + CLEAR) emoji="✅" ;; + *) emoji="⚪️" ;; + esac if [ "${SEND_TELEGRAM}" = "YES" -a ! -z "${bottoken}" -a ! -z "${chatids}" -a ! -z "${message}" ]; then @@ -951,7 +985,7 @@ send_telegram() { httpcode=$(${curl} --write-out %{http_code} --silent --output /dev/null ${disableNotification} \ --data-urlencode "parse_mode=HTML" \ --data-urlencode "disable_web_page_preview=true" \ - --data-urlencode "text=${message}" \ + --data-urlencode "text=${emoji} ${message}" \ "https://api.telegram.org/bot${bottoken}/sendMessage?chat_id=${chatid}") if [ "${httpcode}" == "200" ] @@ -1040,7 +1074,7 @@ EOF # discord sender send_discord() { - local webhook="${1}/slack" channels="${2}" httpcode sent=0 channel color payload + local webhook="${1}/slack" channels="${2}" httpcode sent=0 channel color payload username [ "${SEND_DISCORD}" != "YES" ] && return 1 @@ -1053,10 +1087,13 @@ send_discord() { for channel in ${channels} do + username="netdata on ${host}" + [ ${#username} -gt 32 ] && username="${username:0:29}..." + payload="$(cat </dev/null; url_host="${REPLY}" +urlencode "${host}" >/dev/null; url_host="${REPLY}" urlencode "${chart}" >/dev/null; url_chart="${REPLY}" urlencode "${family}" >/dev/null; url_family="${REPLY}" urlencode "${name}" >/dev/null; url_name="${REPLY}" @@ -1281,6 +1318,24 @@ send_pd "${to_pd}" SENT_PD=$? +# ----------------------------------------------------------------------------- +# send the custom message + +send_custom() { + # is it enabled? + [ "${SEND_CUSTOM}" != "YES" ] && return 1 + + # do we have any sender? + [ -z "${1}" ] && return 1 + + # call the custom_sender function + custom_sender "${@}" +} + +send_custom "${to_custom}" +SENT_CUSTOM=$? + + # ----------------------------------------------------------------------------- # send hipchat message @@ -1295,17 +1350,40 @@ ${host} ${status_message}
    \ SENT_HIPCHAT=$? + # ----------------------------------------------------------------------------- # send the email send_email < - - + + @@ -1316,12 +1394,12 @@ Content-Type: text/html @@ -1330,46 +1408,46 @@ Content-Type: text/html
    -
    netdata notification
    +
    netdata notification
    -

    ${host} ${status_message}

    +

    ${host} ${status_message}

    - - - - - - - - @@ -1404,6 +1482,7 @@ if [ ${SENT_EMAIL} -eq 0 \ -o ${SENT_PUSHBULLET} -eq 0 \ -o ${SENT_KAFKA} -eq 0 \ -o ${SENT_PD} -eq 0 \ + -o ${SENT_CUSTOM} -eq 0 \ ] then # we did send something diff --git a/plugins.d/alarm-test.sh b/plugins.d/alarm-test.sh index 1963111a5..9df5361a9 100755 --- a/plugins.d/alarm-test.sh +++ b/plugins.d/alarm-test.sh @@ -2,7 +2,7 @@ # netdata # real-time performance and health monitoring, done right! -# (C) 2016 Costa Tsaousis +# (C) 2017 Costa Tsaousis # GPL v3+ # # Script to test alarm notifications for netdata diff --git a/plugins.d/cgroup-name.sh b/plugins.d/cgroup-name.sh index a1e3abe08..dc0bf7553 100755 --- a/plugins.d/cgroup-name.sh +++ b/plugins.d/cgroup-name.sh @@ -51,7 +51,7 @@ debug() { # ----------------------------------------------------------------------------- -NETDATA_CONFIG_DIR="${NETDATA_CONFIG_DIR-/etc/netdata}" +[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata" CONFIG="${NETDATA_CONFIG_DIR}/cgroups-names.conf" CGROUP="${1}" NAME= @@ -74,22 +74,22 @@ if [ -f "${CONFIG}" ] # info "configuration file '${CONFIG}' is not available." fi -function get_name_classic { - local DOCKERID="$1" - info "Running command: docker ps --filter=id=\"${DOCKERID}\" --format=\"{{.Names}}\"" - NAME="$( docker ps --filter=id="${DOCKERID}" --format="{{.Names}}" )" +function docker_get_name_classic { + local id="${1}" + info "Running command: docker ps --filter=id=\"${id}\" --format=\"{{.Names}}\"" + NAME="$( docker ps --filter=id="${id}" --format="{{.Names}}" )" return 0 } -function get_name_api { - local DOCKERID="$1" +function docker_get_name_api { + local id="${1}" if [ ! -S "/var/run/docker.sock" ] then warning "Can't find /var/run/docker.sock" return 1 fi - info "Running API command: /containers/${DOCKERID}/json" - JSON=$(echo -e "GET /containers/${DOCKERID}/json HTTP/1.0\r\n" | nc -U /var/run/docker.sock | egrep '^{.*') + info "Running API command: /containers/${id}/json" + JSON=$(echo -e "GET /containers/${id}/json HTTP/1.0\r\n" | nc -U /var/run/docker.sock | grep '^{.*') NAME=$(echo $JSON | jq -r .Name,.Config.Hostname | grep -v null | head -n1 | sed 's|^/||') return 0 } @@ -107,9 +107,9 @@ if [ -z "${NAME}" ] then if hash docker 2>/dev/null then - get_name_classic $DOCKERID + docker_get_name_classic ${DOCKERID} else - get_name_api $DOCKERID || get_name_classic $DOCKERID + docker_get_name_api ${DOCKERID} || docker_get_name_classic ${DOCKERID} fi if [ -z "${NAME}" ] then @@ -123,7 +123,31 @@ if [ -z "${NAME}" ] then # libvirtd / qemu virtual machines - NAME="$(echo ${CGROUP} | sed 's/machine.slice_machine.*-qemu//; s/\/x2d//; s/\/x2d/\-/g; s/\.scope//g')" + # NAME="$(echo ${CGROUP} | sed 's/machine.slice_machine.*-qemu//; s/\/x2d//; s/\/x2d/\-/g; s/\.scope//g')" + NAME="qemu_$(echo ${CGROUP} | sed 's/machine.slice_machine.*-qemu//; s/\/x2d[[:digit:]]*//; s/\/x2d//g; s/\.scope//g')" + + elif [[ "${CGROUP}" =~ qemu.slice_([0-9]+).scope && -d /etc/pve ]] + then + # Proxmox VMs + + FILENAME="/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf" + if [[ -f $FILENAME && -r $FILENAME ]] + then + NAME="qemu_$(grep -e '^name: ' "/etc/pve/qemu-server/${BASH_REMATCH[1]}.conf" | head -1 | sed -rn 's|\s*name\s*:\s*(.*)?$|\1|p')" + else + error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group." + fi + elif [[ "${CGROUP}" =~ lxc_([0-9]+) && -d /etc/pve ]] + then + # Proxmox Containers (LXC) + + FILENAME="/etc/pve/lxc/${BASH_REMATCH[1]}.conf" + if [[ -f ${FILENAME} && -r ${FILENAME} ]] + then + NAME=$(grep -e '^hostname: ' /etc/pve/lxc/${BASH_REMATCH[1]}.conf | head -1 | sed -rn 's|\s*hostname\s*:\s*(.*)?$|\1|p') + else + error "proxmox config file missing ${FILENAME} or netdata does not have read access. Please ensure netdata is a member of www-data group." + fi fi [ -z "${NAME}" ] && NAME="${CGROUP}" diff --git a/plugins.d/charts.d.plugin b/plugins.d/charts.d.plugin index 00206f95f..eda5c0de4 100755 --- a/plugins.d/charts.d.plugin +++ b/plugins.d/charts.d.plugin @@ -2,7 +2,7 @@ # netdata # real-time performance and health monitoring, done right! -# (C) 2016 Costa Tsaousis +# (C) 2017 Costa Tsaousis # GPL v3+ # # charts.d.plugin allows easy development of BASH plugins @@ -115,10 +115,11 @@ info "started from '$PROGRAM_FILE' with options: $*" # internal defaults # netdata exposes a few environment variables for us -pluginsd="${NETDATA_PLUGINS_DIR}" -[ -z "$pluginsd" ] && pluginsd="$( dirname $PROGRAM_FILE )" +[ -z "${NETDATA_PLUGINS_DIR}" ] && NETDATA_PLUGINS_DIR="$(dirname "${0}")" +[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata" -confd="${NETDATA_CONFIG_DIR-/etc/netdata}" +pluginsd="${NETDATA_PLUGINS_DIR}" +confd="${NETDATA_CONFIG_DIR}" chartsd="$pluginsd/../charts.d" myconfig="$confd/$PROGRAM_NAME.conf" diff --git a/plugins.d/fping.plugin b/plugins.d/fping.plugin index 232c00630..b6d981a85 100755 --- a/plugins.d/fping.plugin +++ b/plugins.d/fping.plugin @@ -2,7 +2,7 @@ # netdata # real-time performance and health monitoring, done right! -# (C) 2016 Costa Tsaousis +# (C) 2017 Costa Tsaousis # GPL v3+ # # This plugin requires a latest version of fping. @@ -37,25 +37,15 @@ if [ "${1}" = "install" ] run cd /usr/src - if [ -d fping-3.15 ] + if [ -d fping-4.0 ] then - run rm -rf fping-3.15 || exit 1 + run rm -rf fping-4.0 || exit 1 fi - download 'https://github.com/schweikert/fping/archive/3.15.tar.gz' | run tar -zxvpf - + download 'https://github.com/schweikert/fping/releases/download/v4.0/fping-4.0.tar.gz' | run tar -zxvpf - [ $? -ne 0 ] && exit 1 - run cd fping-3.15 || exit 1 - - #if [ -d fping-ktsaou.git ] - # then - # run cd fping-ktsaou.git - # run git pull - #else - # run git clone https://github.com/ktsaou/fping.git fping-ktsaou.git - # run cd fping-ktsaou.git - #fi - - run ./autogen.sh + run cd fping-4.0 || exit 1 + run ./configure --prefix=/usr/local run make clean run make @@ -139,7 +129,7 @@ update_every="${1-1}" # the netdata configuration directory # passed by netdata as an environment variable -NETDATA_CONFIG_DIR="${NETDATA_CONFIG_DIR-/etc/netdata}" +[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata" # ----------------------------------------------------------------------------- # configuration options @@ -173,7 +163,7 @@ source "${NETDATA_CONFIG_DIR}/${plugin}.conf" if [ -z "${hosts}" ] then - fatal "no hosts configued in '${NETDATA_CONFIG_DIR}/${plugin}.conf' - nothing to do." + fatal "no hosts configured in '${NETDATA_CONFIG_DIR}/${plugin}.conf' - nothing to do." fi if [ -z "${fping}" -o ! -x "${fping}" ] diff --git a/plugins.d/node.d.plugin b/plugins.d/node.d.plugin index 8b7047fcb..b16203912 100755 --- a/plugins.d/node.d.plugin +++ b/plugins.d/node.d.plugin @@ -4,13 +4,13 @@ // shebang hack from: // http://unix.stackexchange.com/questions/65235/universal-node-js-shebang -// Initially this is run as a shell script (#!/bin/sh). +// Initially this is run as a shell script. // Then, the second line, finds nodejs or node or js in the system path // and executes it with the shell parameters. // netdata // real-time performance and health monitoring, done right! -// (C) 2016 Costa Tsaousis +// (C) 2017 Costa Tsaousis // GPL v3+ // -------------------------------------------------------------------------------------------------------------------- @@ -21,7 +21,7 @@ // get NETDATA environment variables var NETDATA_PLUGINS_DIR = process.env.NETDATA_PLUGINS_DIR || __dirname; -var NETDATA_CONFIG_DIR = process.env.NETDATA_CONFIG_DIR || '/etc/netdata'; +var NETDATA_CONFIG_DIR = process.env.NETDATA_CONFIG_DIR || __dirname + '/../../../../etc/netdata'; var NETDATA_UPDATE_EVERY = process.env.NETDATA_UPDATE_EVERY || 1; var NODE_D_DIR = NETDATA_PLUGINS_DIR + '/../node.d'; diff --git a/plugins.d/python.d.plugin b/plugins.d/python.d.plugin index efa62cbc5..03c156f41 100755 --- a/plugins.d/python.d.plugin +++ b/plugins.d/python.d.plugin @@ -20,8 +20,11 @@ BASE_CONFIG = {'update_every': os.getenv('NETDATA_UPDATE_EVERY', 1), 'retries': 10} MODULES_DIR = os.path.abspath(os.getenv('NETDATA_PLUGINS_DIR', - os.path.dirname(__file__)) + "/../python.d") + "/" -CONFIG_DIR = os.getenv('NETDATA_CONFIG_DIR', "/etc/netdata/") + os.path.dirname(__file__)) + "/../python.d") + "/" + +CONFIG_DIR = os.getenv('NETDATA_CONFIG_DIR', + os.path.dirname(__file__) + "/../../../../etc/netdata") + # directories should end with '/' if CONFIG_DIR[-1] != "/": CONFIG_DIR += "/" @@ -307,7 +310,7 @@ class PythonCharts(object): ("/" + str(name) if name is not None else "") + ": cannot start job: '" + str(e)) - return None + continue else: # set chart_name (needed to plot run time graphs) job.chart_name = module.__name__ @@ -460,11 +463,11 @@ def read_config(path): config = ordered_load(stream, yaml.SafeLoader) else: config = yaml.load(stream) - except (OSError, IOError): - msg.error(str(path), "is not a valid configuration file") + except (OSError, IOError) as error: + msg.error(str(path), 'reading error:', str(error)) return None - except yaml.YAMLError as e: - msg.error(str(path), "is malformed:", e) + except yaml.YAMLError as error: + msg.error(str(path), "is malformed:", str(error)) return None return config @@ -516,7 +519,7 @@ def run(): global DEBUG_FLAG, TRACE_FLAG, BASE_CONFIG # read configuration file - disabled = ['nginx_log', 'gunicorn_log'] + disabled = ['nginx_log', 'gunicorn_log', 'apache_cache'] enabled = list() default_run = True configfile = CONFIG_DIR + "python.d.conf" diff --git a/plugins.d/tc-qos-helper.sh b/plugins.d/tc-qos-helper.sh index 074fece9a..9153f22e2 100755 --- a/plugins.d/tc-qos-helper.sh +++ b/plugins.d/tc-qos-helper.sh @@ -2,7 +2,7 @@ # netdata # real-time performance and health monitoring, done right! -# (C) 2016 Costa Tsaousis +# (C) 2017 Costa Tsaousis # GPL v3+ # # This script is a helper to allow netdata collect tc data. @@ -101,10 +101,11 @@ debug() { # ----------------------------------------------------------------------------- -plugins_dir="${NETDATA_PLUGINS_DIR}" -[ -z "$plugins_dir" ] && plugins_dir="$( dirname $PROGRAM_FILE )" +[ -z "${NETDATA_PLUGINS_DIR}" ] && NETDATA_PLUGINS_DIR="$(dirname "${0}")" +[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata" -config_dir=${NETDATA_CONFIG_DIR-/etc/netdata} +plugins_dir="${NETDATA_PLUGINS_DIR}" +config_dir="${NETDATA_CONFIG_DIR}" tc="$(which tc 2>/dev/null || command -v tc 2>/dev/null)" diff --git a/python.d/Makefile.am b/python.d/Makefile.am index bfe28ff28..43f25cffe 100644 --- a/python.d/Makefile.am +++ b/python.d/Makefile.am @@ -18,12 +18,14 @@ dist_python_DATA = \ bind_rndc.chart.py \ cpufreq.chart.py \ cpuidle.chart.py \ + dns_query_time.chart.py \ dovecot.chart.py \ elasticsearch.chart.py \ example.chart.py \ exim.chart.py \ fail2ban.chart.py \ freeradius.chart.py \ + go_expvar.chart.py \ haproxy.chart.py \ hddtemp.chart.py \ ipfs.chart.py \ @@ -38,8 +40,10 @@ dist_python_DATA = \ phpfpm.chart.py \ postfix.chart.py \ postgres.chart.py \ + rabbitmq.chart.py \ redis.chart.py \ retroshare.chart.py \ + samba.chart.py \ sensors.chart.py \ squid.chart.py \ smartd_log.chart.py \ diff --git a/python.d/Makefile.in b/python.d/Makefile.in index 9b7846682..33efd42d9 100644 --- a/python.d/Makefile.in +++ b/python.d/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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. @@ -17,6 +16,51 @@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -35,10 +79,10 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -DIST_COMMON = $(dist_python_DATA) $(dist_python_SCRIPTS) \ - $(dist_pythonmodules_DATA) $(dist_pythonyaml2_DATA) \ - $(dist_pythonyaml3_DATA) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in $(top_srcdir)/build/subst.inc +DIST_COMMON = $(top_srcdir)/build/subst.inc $(srcdir)/Makefile.in \ + $(srcdir)/Makefile.am $(dist_python_SCRIPTS) \ + $(dist_python_DATA) $(dist_pythonmodules_DATA) \ + $(dist_pythonyaml2_DATA) $(dist_pythonyaml3_DATA) subdir = python.d ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ @@ -86,13 +130,32 @@ am__installdirs = "$(DESTDIR)$(pythondir)" "$(DESTDIR)$(pythondir)" \ "$(DESTDIR)$(pythonmodulesdir)" "$(DESTDIR)$(pythonyaml2dir)" \ "$(DESTDIR)$(pythonyaml3dir)" SCRIPTS = $(dist_python_SCRIPTS) +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 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac DATA = $(dist_python_DATA) $(dist_pythonmodules_DATA) \ $(dist_pythonyaml2_DATA) $(dist_pythonyaml3_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -251,12 +314,14 @@ dist_python_DATA = \ bind_rndc.chart.py \ cpufreq.chart.py \ cpuidle.chart.py \ + dns_query_time.chart.py \ dovecot.chart.py \ elasticsearch.chart.py \ example.chart.py \ exim.chart.py \ fail2ban.chart.py \ freeradius.chart.py \ + go_expvar.chart.py \ haproxy.chart.py \ hddtemp.chart.py \ ipfs.chart.py \ @@ -271,8 +336,10 @@ dist_python_DATA = \ phpfpm.chart.py \ postfix.chart.py \ postgres.chart.py \ + rabbitmq.chart.py \ redis.chart.py \ retroshare.chart.py \ + samba.chart.py \ sensors.chart.py \ squid.chart.py \ smartd_log.chart.py \ @@ -368,8 +435,11 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_pythonSCRIPTS: $(dist_python_SCRIPTS) @$(NORMAL_INSTALL) - test -z "$(pythondir)" || $(MKDIR_P) "$(DESTDIR)$(pythondir)" @list='$(dist_python_SCRIPTS)'; test -n "$(pythondir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pythondir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pythondir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ @@ -400,8 +470,11 @@ uninstall-dist_pythonSCRIPTS: dir='$(DESTDIR)$(pythondir)'; $(am__uninstall_files_from_dir) install-dist_pythonDATA: $(dist_python_DATA) @$(NORMAL_INSTALL) - test -z "$(pythondir)" || $(MKDIR_P) "$(DESTDIR)$(pythondir)" @list='$(dist_python_DATA)'; test -n "$(pythondir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pythondir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pythondir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -418,8 +491,11 @@ uninstall-dist_pythonDATA: dir='$(DESTDIR)$(pythondir)'; $(am__uninstall_files_from_dir) install-dist_pythonmodulesDATA: $(dist_pythonmodules_DATA) @$(NORMAL_INSTALL) - test -z "$(pythonmodulesdir)" || $(MKDIR_P) "$(DESTDIR)$(pythonmodulesdir)" @list='$(dist_pythonmodules_DATA)'; test -n "$(pythonmodulesdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pythonmodulesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pythonmodulesdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -436,8 +512,11 @@ uninstall-dist_pythonmodulesDATA: dir='$(DESTDIR)$(pythonmodulesdir)'; $(am__uninstall_files_from_dir) install-dist_pythonyaml2DATA: $(dist_pythonyaml2_DATA) @$(NORMAL_INSTALL) - test -z "$(pythonyaml2dir)" || $(MKDIR_P) "$(DESTDIR)$(pythonyaml2dir)" @list='$(dist_pythonyaml2_DATA)'; test -n "$(pythonyaml2dir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pythonyaml2dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pythonyaml2dir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -454,8 +533,11 @@ uninstall-dist_pythonyaml2DATA: dir='$(DESTDIR)$(pythonyaml2dir)'; $(am__uninstall_files_from_dir) install-dist_pythonyaml3DATA: $(dist_pythonyaml3_DATA) @$(NORMAL_INSTALL) - test -z "$(pythonyaml3dir)" || $(MKDIR_P) "$(DESTDIR)$(pythonyaml3dir)" @list='$(dist_pythonyaml3_DATA)'; test -n "$(pythonyaml3dir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pythonyaml3dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pythonyaml3dir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -470,11 +552,11 @@ uninstall-dist_pythonyaml3DATA: @list='$(dist_pythonyaml3_DATA)'; test -n "$(pythonyaml3dir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pythonyaml3dir)'; $(am__uninstall_files_from_dir) -tags: TAGS -TAGS: +tags TAGS: + +ctags CTAGS: -ctags: CTAGS -CTAGS: +cscope cscopelist: distdir: $(DISTFILES) @@ -618,20 +700,20 @@ uninstall-am: uninstall-dist_pythonDATA uninstall-dist_pythonSCRIPTS \ .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-dist_pythonDATA install-dist_pythonSCRIPTS \ - install-dist_pythonmodulesDATA install-dist_pythonyaml2DATA \ - install-dist_pythonyaml3DATA 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-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ - pdf-am ps ps-am uninstall uninstall-am \ - uninstall-dist_pythonDATA uninstall-dist_pythonSCRIPTS \ - uninstall-dist_pythonmodulesDATA \ +.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ + ctags-am distclean distclean-generic distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dist_pythonDATA \ + install-dist_pythonSCRIPTS install-dist_pythonmodulesDATA \ + install-dist_pythonyaml2DATA install-dist_pythonyaml3DATA \ + 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-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags-am \ + uninstall uninstall-am uninstall-dist_pythonDATA \ + uninstall-dist_pythonSCRIPTS uninstall-dist_pythonmodulesDATA \ uninstall-dist_pythonyaml2DATA uninstall-dist_pythonyaml3DATA .in: diff --git a/python.d/README.md b/python.d/README.md index 7df6e3e86..c4504a7c5 100644 --- a/python.d/README.md +++ b/python.d/README.md @@ -227,6 +227,16 @@ Your kernel needs to have `CONFIG_CPU_IDLE` enabled. It produces one stacked chart per CPU, showing the percentage of time spent in each state. +--- +# dns_query_time + +This module provides dns query time statistics. + +**Requirement:** +* `python-dnspython` package + +It produces one aggregate chart or one chart per dns server, showing the query time. + --- # dovecot @@ -473,6 +483,44 @@ and restart/reload your FREERADIUS server. --- +# go_expvar + +--- + +The `go_expvar` module can monitor any Go application that exposes its metrics with the use of `expvar` package from the Go standard library. + +`go_expvar` produces charts for Go runtime memory statistics and optionally any number of custom charts. Please see the [wiki page](https://github.com/firehol/netdata/wiki/Monitoring-Go-Applications) for more info. + +For the memory statistics, it produces the following charts: + +1. **Heap allocations** in kB + * alloc: size of objects allocated on the heap + * inuse: size of allocated heap spans + +2. **Stack allocations** in kB + * inuse: size of allocated stack spans + +3. **MSpan allocations** in kB + * inuse: size of allocated mspan structures + +4. **MCache allocations** in kB + * inuse: size of allocated mcache structures + +5. **Virtual memory** in kB + * sys: size of reserved virtual address space + +6. **Live objects** + * live: number of live objects in memory + +7. **GC pauses average** in ns + * avg: average duration of all GC stop-the-world pauses + +### configuration + +Please see the [wiki page](https://github.com/firehol/netdata/wiki/Monitoring-Go-Applications#using-netdata-go_expvar-module) for detailed info about module configuration. + +--- + # haproxy Module monitors frontend and backend metrics such as bytes in, bytes out, sessions current, sessions in queue current. @@ -1198,6 +1246,60 @@ When no configuration file is found, module tries to connect to TCP/IP socket: ` --- +# rabbitmq + +Module monitor rabbitmq performance and health metrics. + +Following charts are drawn: + +1. **Queued Messages** + * ready + * unacknowledged + +2. **Message Rates** + * ack + * redelivered + * deliver + * publish + +3. **Global Counts** + * channels + * consumers + * connections + * queues + * exchanges + +4. **File Descriptors** + * used descriptors + +5. **Socket Descriptors** + * used descriptors + +6. **Erlang processes** + * used processes + +7. **Memory** + * free memory in megabytes + +8. **Disk Space** + * free disk space in gigabytes + +### configuration + +```yaml +socket: + name : 'local' + host : '127.0.0.1' + port : 15672 + user : 'guest' + pass : 'guest' + +``` + +When no configuration file is found, module tries to connect to: `localhost:15672`. + +--- + # redis Get INFO data from redis instance. @@ -1241,6 +1343,68 @@ When no configuration file is found, module tries to connect to TCP/IP socket: ` --- +# samba + +Performance metrics of Samba file sharing. + +It produces the following charts: + +1. **Syscall R/Ws** in kilobytes/s + * sendfile + * recvfle + +2. **Smb2 R/Ws** in kilobytes/s + * readout + * writein + * readin + * writeout + +3. **Smb2 Create/Close** in operations/s + * create + * close + +4. **Smb2 Info** in operations/s + * getinfo + * setinfo + +5. **Smb2 Find** in operations/s + * find + +6. **Smb2 Notify** in operations/s + * notify + +7. **Smb2 Lesser Ops** as counters + * tcon + * negprot + * tdis + * cancel + * logoff + * flush + * lock + * keepalive + * break + * sessetup + +### configuration + +Requires that smbd has been compiled with profiling enabled. Also required +that `smbd` was started either with the `-P 1` option or inside `smb.conf` +using `smbd profiling level`. + +This plugin uses `smbstatus -P` which can only be executed by root. It uses +sudo and assumes that it is configured such that the `netdata` user can +execute smbstatus as root without password. + +For example: + + netdata ALL=(ALL) NOPASSWD: /usr/bin/smbstatus -P + +```yaml +update_every : 5 # update frequency +``` + +--- + # sensors System sensors information. @@ -1251,6 +1415,12 @@ Charts are created dynamically. For detailed configuration information please read [`sensors.conf`](https://github.com/firehol/netdata/blob/master/conf.d/python.d/sensors.conf) file. +### possible issues + +There have been reports from users that on certain servers, ACPI ring buffer errors are printed by the kernel (`dmesg`) when ACPI sensors are being accessed. +We are tracking such cases in issue [#827](https://github.com/firehol/netdata/issues/827). +Please join this discussion for help. + --- # squid diff --git a/python.d/apache.chart.py b/python.d/apache.chart.py index 2e4d16dd3..71fe03001 100644 --- a/python.d/apache.chart.py +++ b/python.d/apache.chart.py @@ -22,7 +22,8 @@ ORDER = ['requests', 'connections', 'conns_async', 'net', 'workers', 'reqpersec' CHARTS = { 'bytesperreq': { - 'options': [None, 'apache Lifetime Avg. Response Size', 'bytes/request', 'statistics', 'apache.bytesperreq', 'area'], + 'options': [None, 'apache Lifetime Avg. Response Size', 'bytes/request', + 'statistics', 'apache.bytesperreq', 'area'], 'lines': [ ["size_req"] ]}, @@ -30,15 +31,19 @@ CHARTS = { 'options': [None, 'apache Workers', 'workers', 'workers', 'apache.workers', 'stacked'], 'lines': [ ["idle"], - ["busy"] + ["idle_servers", 'idle'], + ["busy"], + ["busy_servers", 'busy'] ]}, 'reqpersec': { - 'options': [None, 'apache Lifetime Avg. Requests/s', 'requests/s', 'statistics', 'apache.reqpersec', 'area'], + 'options': [None, 'apache Lifetime Avg. Requests/s', 'requests/s', 'statistics', + 'apache.reqpersec', 'area'], 'lines': [ ["requests_sec"] ]}, 'bytespersec': { - 'options': [None, 'apache Lifetime Avg. Bandwidth/s', 'kilobytes/s', 'statistics', 'apache.bytesperreq', 'area'], + 'options': [None, 'apache Lifetime Avg. Bandwidth/s', 'kilobytes/s', 'statistics', + 'apache.bytesperreq', 'area'], 'lines': [ ["size_sec", None, 'absolute', 1, 1000] ]}, @@ -66,43 +71,54 @@ CHARTS = { ]} } +ASSIGNMENT = {"BytesPerReq": 'size_req', + "IdleWorkers": 'idle', + "IdleServers": 'idle_servers', + "BusyWorkers": 'busy', + "BusyServers": 'busy_servers', + "ReqPerSec": 'requests_sec', + "BytesPerSec": 'size_sec', + "Total Accesses": 'requests', + "Total kBytes": 'sent', + "ConnsTotal": 'connections', + "ConnsAsyncKeepAlive": 'keepalive', + "ConnsAsyncClosing": 'closing', + "ConnsAsyncWriting": 'writing'} + class Service(UrlService): def __init__(self, configuration=None, name=None): UrlService.__init__(self, configuration=configuration, name=name) - if len(self.url) == 0: - self.url = "http://localhost/server-status?auto" self.order = ORDER self.definitions = CHARTS - self.assignment = {"BytesPerReq": 'size_req', - "IdleWorkers": 'idle', - "BusyWorkers": 'busy', - "ReqPerSec": 'requests_sec', - "BytesPerSec": 'size_sec', - "Total Accesses": 'requests', - "Total kBytes": 'sent', - "ConnsTotal": 'connections', - "ConnsAsyncKeepAlive": 'keepalive', - "ConnsAsyncClosing": 'closing', - "ConnsAsyncWriting": 'writing'} + self.url = self.configuration.get('url', 'http://localhost/server-status?auto') + + def check(self): + if UrlService.check(self): + if 'idle_servers' in self._data_from_check: + self.__module__ = 'lighttpd' + for chart in self.definitions: + opts = self.definitions[chart]['options'] + opts[1] = opts[1].replace('apache', 'lighttpd') + opts[4] = opts[4].replace('apache', 'lighttpd') + return True + return False def _get_data(self): """ Format data received from http request :return: dict """ - try: - raw = self._get_raw_data().split("\n") - except AttributeError: + raw_data = self._get_raw_data() + if not raw_data: return None - data = {} - for row in raw: + data = dict() + + for row in raw_data.split('\n'): tmp = row.split(":") - if str(tmp[0]) in self.assignment: + if tmp[0] in ASSIGNMENT: try: - data[self.assignment[tmp[0]]] = int(float(tmp[1])) + data[ASSIGNMENT[tmp[0]]] = int(float(tmp[1])) except (IndexError, ValueError): - pass - if len(data) == 0: - return None - return data + continue + return data or None diff --git a/python.d/bind_rndc.chart.py b/python.d/bind_rndc.chart.py index a4d753703..5a9749287 100644 --- a/python.d/bind_rndc.chart.py +++ b/python.d/bind_rndc.chart.py @@ -2,100 +2,141 @@ # Description: bind rndc netdata python.d module # Author: l2isbad -from base import SimpleService -from re import compile, findall -from os.path import getsize, split -from os import access as is_accessible, R_OK +from os.path import getsize +from os import access, R_OK from subprocess import Popen +from collections import defaultdict +from base import SimpleService priority = 60000 retries = 60 update_every = 30 -NMS = ['requests', 'responses', 'success', 'auth_answer', 'nonauth_answer', 'nxrrset', 'failure', - 'nxdomain', 'recursion', 'duplicate', 'rejections'] -QUERIES = ['RESERVED0', 'A', 'NS', 'CNAME', 'SOA', 'PTR', 'MX', 'TXT', 'X25', 'AAAA', 'SRV', 'NAPTR', - 'A6', 'DS', 'RRSIG', 'DNSKEY', 'SPF', 'ANY', 'DLV'] +ORDER = ['name_server_statistics', 'incoming_queries', 'outgoing_queries', 'named_stats_size'] + +CHARTS = { + 'name_server_statistics': { + 'options': [None, 'Name Server Statistics', 'stats', 'name server statistics', + 'bind_rndc.name_server_statistics', 'line'], + 'lines': [ + ['nms_requests', 'requests', 'incremental'], + ['nms_rejected_queries', 'rejected_queries', 'incremental'], + ['nms_success', 'success', 'incremental'], + ['nms_failure', 'failure', 'incremental'], + ['nms_responses', 'responses', 'incremental'], + ['nms_duplicate', 'duplicate', 'incremental'], + ['nms_recursion', 'recursion', 'incremental'], + ['nms_nxrrset', 'nxrrset', 'incremental'], + ['nms_nxdomain', 'nxdomain', 'incremental'], + ['nms_non_auth_answer', 'non_auth_answer', 'incremental'], + ['nms_auth_answer', 'auth_answer', 'incremental'], + ['nms_dropped_queries', 'dropped_queries', 'incremental'], + ]}, + 'incoming_queries': { + 'options': [None, 'Incoming Queries', 'queries', 'incoming queries', + 'bind_rndc.incoming_queries', 'line'], + 'lines': [ + ]}, + 'outgoing_queries': { + 'options': [None, 'Outgoing Queries', 'queries', 'outgoing queries', + 'bind_rndc.outgoing_queries', 'line'], + 'lines': [ + ]}, + 'named_stats_size': { + 'options': [None, 'Named Stats File Size', 'MB', 'file size', + 'bind_rndc.stats_size', 'line'], + 'lines': [ + ['stats_size', None, 'absolute', 1, 1 << 20] + ]} +} + +NMS = { + 'nms_requests': + ['IPv4 requests received', + 'IPv6 requests received', + 'TCP requests received', + 'requests with EDNS(0) receive'], + 'nms_responses': + ['responses sent', + 'truncated responses sent', + 'responses with EDNS(0) sent', + 'requests with unsupported EDNS version received'], + 'nms_failure': + ['other query failures', + 'queries resulted in SERVFAIL'], + 'nms_auth_answer': + ['queries resulted in authoritative answer'], + 'nms_non_auth_answer': + ['queries resulted in non authoritative answer'], + 'nms_nxrrset': + ['queries resulted in nxrrset'], + 'nms_success': + ['queries resulted in successful answer'], + 'nms_nxdomain': + ['queries resulted in NXDOMAIN'], + 'nms_recursion': + ['queries caused recursion'], + 'nms_duplicate': + ['duplicate queries received'], + 'nms_rejected_queries': + ['auth queries rejected', + 'recursive queries rejected'], + 'nms_dropped_queries': + ['queries dropped'] +} + +STATS = ['Name Server Statistics', 'Incoming Queries', 'Outgoing Queries'] class Service(SimpleService): def __init__(self, configuration=None, name=None): SimpleService.__init__(self, configuration=configuration, name=name) + self.order = ORDER + self.definitions = CHARTS self.named_stats_path = self.configuration.get('named_stats_path', '/var/log/bind/named.stats') - self.regex_values = compile(r'([0-9]+) ([^\n]+)') - # self.options = ['Incoming Requests', 'Incoming Queries', 'Outgoing Queries', - # 'Name Server Statistics', 'Zone Maintenance Statistics', 'Resolver Statistics', - # 'Cache DB RRsets', 'Socket I/O Statistics'] - self.options = ['Name Server Statistics', 'Incoming Queries', 'Outgoing Queries'] - self.regex_options = [r'(%s(?= \+\+)) \+\+([^\+]+)' % option for option in self.options] self.rndc = self.find_binary('rndc') + self.data = dict(nms_requests=0, nms_responses=0, nms_failure=0, nms_auth=0, + nms_non_auth=0, nms_nxrrset=0, nms_success=0, nms_nxdomain=0, + nms_recursion=0, nms_duplicate=0, nms_rejected_queries=0, + nms_dropped_queries=0) def check(self): - # We cant start without 'rndc' command if not self.rndc: self.error('Can\'t locate \'rndc\' binary or binary is not executable by netdata') return False - # We cant start if stats file is not exist or not readable by netdata user - if not is_accessible(self.named_stats_path, R_OK): + if not access(self.named_stats_path, R_OK): self.error('Cannot access file %s' % self.named_stats_path) return False - size_before = getsize(self.named_stats_path) run_rndc = Popen([self.rndc, 'stats'], shell=False) run_rndc.wait() - size_after = getsize(self.named_stats_path) - # We cant start if netdata user has no permissions to run 'rndc stats' if not run_rndc.returncode: - # 'rndc' was found, stats file is exist and readable and we can run 'rndc stats'. Lets go! - self.create_charts() - - # BIND APPEND dump on every run 'rndc stats' - # that is why stats file size can be VERY large if update_interval too small - dump_size_24hr = round(86400 / self.update_every * (int(size_after) - int(size_before)) / 1048576, 3) - - # If update_every too small we should WARN user - if self.update_every < 30: - self.info('Update_every %s is NOT recommended for use. Increase the value to > 30' % self.update_every) - - self.info('With current update_interval it will be + %s MB every 24hr. ' - 'Don\'t forget to create logrotate conf file for %s' % (dump_size_24hr, self.named_stats_path)) - - self.info('Plugin was started successfully.') - return True - else: - self.error('Not enough permissions to run "%s stats"' % self.rndc) - return False + self.error('Not enough permissions to run "%s stats"' % self.rndc) + return False def _get_raw_data(self): """ Run 'rndc stats' and read last dump from named.stats - :return: tuple( - file.read() obj, - named.stats file size - ) + :return: dict """ + result = dict() try: current_size = getsize(self.named_stats_path) - except OSError: - return None, None - - run_rndc = Popen([self.rndc, 'stats'], shell=False) - run_rndc.wait() - - if run_rndc.returncode: - return None, None - - try: - with open(self.named_stats_path) as bind_rndc: - bind_rndc.seek(current_size) - result = bind_rndc.read() - except OSError: - return None, None - else: - return result, current_size + run_rndc = Popen([self.rndc, 'stats'], shell=False) + run_rndc.wait() + + if run_rndc.returncode: + return None + with open(self.named_stats_path) as named_stats: + named_stats.seek(current_size) + result['stats'] = named_stats.readlines() + result['size'] = current_size + return result + except (OSError, IOError): + return None def _get_data(self): """ @@ -103,72 +144,98 @@ class Service(SimpleService): :return: dict """ - raw_data, size = self._get_raw_data() + raw_data = self._get_raw_data() if raw_data is None: return None - - rndc_stats = dict() - - # Result: dict. - # topic = Cache DB RRsets; body = A 178303 NS 86790 ... ; desc = A; value = 178303 - # {'Cache DB RRsets': [('A', 178303), ('NS', 286790), ...], - # {Incoming Queries': [('RESERVED0', 8), ('A', 4557317680), ...], - # ...... - for regex in self.regex_options: - rndc_stats.update(dict([(topic, [(desc, int(value)) for value, desc in self.regex_values.findall(body)]) - for topic, body in findall(regex, raw_data)])) - - nms = dict(rndc_stats.get('Name Server Statistics', [])) - - inc_queries = dict([('i' + k, 0) for k in QUERIES]) - inc_queries.update(dict([('i' + k, v) for k, v in rndc_stats.get('Incoming Queries', [])])) - out_queries = dict([('o' + k, 0) for k in QUERIES]) - out_queries.update(dict([('o' + k, v) for k, v in rndc_stats.get('Outgoing Queries', [])])) - - to_netdata = dict() - to_netdata['requests'] = sum([v for k, v in nms.items() if 'request' in k and 'received' in k]) - to_netdata['responses'] = sum([v for k, v in nms.items() if 'responses' in k and 'sent' in k]) - to_netdata['success'] = nms.get('queries resulted in successful answer', 0) - to_netdata['auth_answer'] = nms.get('queries resulted in authoritative answer', 0) - to_netdata['nonauth_answer'] = nms.get('queries resulted in non authoritative answer', 0) - to_netdata['nxrrset'] = nms.get('queries resulted in nxrrset', 0) - to_netdata['failure'] = sum([nms.get('queries resulted in SERVFAIL', 0), nms.get('other query failures', 0)]) - to_netdata['nxdomain'] = nms.get('queries resulted in NXDOMAIN', 0) - to_netdata['recursion'] = nms.get('queries caused recursion', 0) - to_netdata['duplicate'] = nms.get('duplicate queries received', 0) - to_netdata['rejections'] = nms.get('recursive queries rejected', 0) - to_netdata['stats_size'] = size - - to_netdata.update(inc_queries) - to_netdata.update(out_queries) - return to_netdata - - def create_charts(self): - self.order = ['stats_size', 'bind_stats', 'incoming_q', 'outgoing_q'] - self.definitions = { - 'bind_stats': { - 'options': [None, 'Name Server Statistics', 'stats', 'name server statistics', 'bind_rndc.stats', 'line'], - 'lines': [ - ]}, - 'incoming_q': { - 'options': [None, 'Incoming queries', 'queries','incoming queries', 'bind_rndc.incq', 'line'], - 'lines': [ - ]}, - 'outgoing_q': { - 'options': [None, 'Outgoing queries', 'queries','outgoing queries', 'bind_rndc.outq', 'line'], - 'lines': [ - ]}, - 'stats_size': { - 'options': [None, '%s file size' % split(self.named_stats_path)[1].capitalize(), 'megabytes', - '%s size' % split(self.named_stats_path)[1], 'bind_rndc.size', 'line'], - 'lines': [ - ["stats_size", None, "absolute", 1, 1048576] - ]} - } - for elem in QUERIES: - self.definitions['incoming_q']['lines'].append(['i' + elem, elem, 'incremental']) - self.definitions['outgoing_q']['lines'].append(['o' + elem, elem, 'incremental']) - - for elem in NMS: - self.definitions['bind_stats']['lines'].append([elem, None, 'incremental']) + parsed = dict() + for stat in STATS: + parsed[stat] = parse_stats(field=stat, + named_stats=raw_data['stats']) + + self.data.update(nms_mapper(data=parsed['Name Server Statistics'])) + + for elem in zip(['Incoming Queries', 'Outgoing Queries'], ['incoming_queries', 'outgoing_queries']): + parsed_key, chart_name = elem[0], elem[1] + for dimension_id, value in queries_mapper(data=parsed[parsed_key], + add=chart_name[:9]).items(): + if dimension_id not in self.data: + dimension = dimension_id.replace(chart_name[:9], '') + self._add_new_dimension(dimension_id=dimension_id, + dimension=dimension, + chart_name=chart_name, + priority=self.priority + self.order.index(chart_name)) + self.data[dimension_id] = value + + self.data['stats_size'] = raw_data['size'] + return self.data + + +def parse_stats(field, named_stats): + """ + :param field: str: + :param named_stats: list: + :return: dict + + Example: + filed: 'Incoming Queries' + names_stats (list of lines): + ++ Incoming Requests ++ + 1405660 QUERY + 3 NOTIFY + ++ Incoming Queries ++ + 1214961 A + 75 NS + 2 CNAME + 2897 SOA + 35544 PTR + 14 MX + 5822 TXT + 145974 AAAA + 371 SRV + ++ Outgoing Queries ++ + ... + + result: + {'A', 1214961, 'NS': 75, 'CNAME': 2, 'SOA': 2897, ...} + """ + data = dict() + ns = iter(named_stats) + for line in ns: + if field not in line: + continue + while True: + try: + line = next(ns) + except StopIteration: + break + if '++' not in line: + if '[' in line: + continue + v, k = line.strip().split(' ', 1) + data[k] = int(v) + continue + break + break + return data + + +def nms_mapper(data): + """ + :param data: dict + :return: dict(defaultdict) + """ + result = defaultdict(int) + for k, v in NMS.items(): + for elem in v: + result[k] += data.get(elem, 0) + return result + + +def queries_mapper(data, add): + """ + :param data: dict + :param add: str + :return: dict + """ + return dict([(add + k, v) for k, v in data.items()]) diff --git a/python.d/cpufreq.chart.py b/python.d/cpufreq.chart.py index e28bdea8f..d5544b7ba 100644 --- a/python.d/cpufreq.chart.py +++ b/python.d/cpufreq.chart.py @@ -100,7 +100,7 @@ class Service(SimpleService): self.error("couldn't find a method to read cpufreq statistics") return False - for name in self.assignment.keys(): + for name in sorted(self.assignment, key=lambda v: int(v[3:])): self.definitions[ORDER[0]]['lines'].append([name, name, 'absolute', 1, 1000]) return True diff --git a/python.d/dns_query_time.chart.py b/python.d/dns_query_time.chart.py new file mode 100644 index 000000000..9053d9a1b --- /dev/null +++ b/python.d/dns_query_time.chart.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +# Description: dns_query_time netdata python.d module +# Author: l2isbad + +try: + from time import monotonic as time +except ImportError: + from time import time +try: + import dns.message, dns.query, dns.name + DNS_PYTHON = True +except ImportError: + DNS_PYTHON = False +try: + from queue import Queue +except ImportError: + from Queue import Queue +from random import choice +from threading import Thread +from socket import gethostbyname, gaierror +from base import SimpleService + + +# default module values (can be overridden per job in `config`) +update_every = 5 +priority = 60000 +retries = 60 + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.order = list() + self.definitions = dict() + self.timeout = self.configuration.get('response_timeout', 4) + self.aggregate = self.configuration.get('aggregate', True) + self.domains = self.configuration.get('domains') + self.server_list = self.configuration.get('dns_servers') + + def check(self): + if not DNS_PYTHON: + self.error('\'python-dnspython\' package is needed to use dns_query_time.chart.py') + return False + + self.timeout = self.timeout if isinstance(self.timeout, int) else 4 + self.update_every = self.timeout + 1 if self.update_every <= self.timeout else self.update_every + + if not all([self.domains, self.server_list, + isinstance(self.server_list, str), isinstance(self.domains, str)]): + self.error('server_list and domain_list can\'t be empty') + return False + else: + self.domains, self.server_list = self.domains.split(), self.server_list.split() + + for ns in self.server_list: + if not check_ns(ns): + self.info('Bad NS: %s' % ns) + self.server_list.remove(ns) + if not self.server_list: + return False + + data = self._get_data(timeout=1) + + down_servers = [s for s in data if data[s] == -100] + for down in down_servers: + down = down[3:].replace('_', '.') + self.info('Removed due to non response %s' % down) + self.server_list.remove(down) + if not self.server_list: + return False + + self._data_from_check = data + self.order, self.definitions = create_charts(aggregate=self.aggregate, server_list=self.server_list) + self.info(str({'domains': len(self.domains), 'servers': self.server_list})) + return True + + def _get_data(self, timeout=None): + return dns_request(self.server_list, timeout or self.timeout, self.domains) + + +def dns_request(server_list, timeout, domains): + threads = list() + que = Queue() + result = dict() + + def dns_req(ns, t, q): + domain = dns.name.from_text(choice(domains)) + request = dns.message.make_query(domain, dns.rdatatype.A) + + try: + dns_start = time() + dns.query.udp(request, ns, timeout=t) + dns_end = time() + query_time = round((dns_end - dns_start) * 1000) + q.put({'_'.join(['ns', ns.replace('.', '_')]): query_time}) + except dns.exception.Timeout: + q.put({'_'.join(['ns', ns.replace('.', '_')]): -100}) + + for server in server_list: + th = Thread(target=dns_req, args=(server, timeout, que)) + th.start() + threads.append(th) + + for th in threads: + th.join() + result.update(que.get()) + + return result + + +def check_ns(ns): + try: + return gethostbyname(ns) + except gaierror: + return False + + +def create_charts(aggregate, server_list): + if aggregate: + order = ['dns_group'] + definitions = {'dns_group': {'options': [None, 'DNS Response Time', 'ms', 'name servers', + 'dns_query_time.response_time', 'line'], 'lines': []}} + for ns in server_list: + definitions['dns_group']['lines'].append(['_'.join(['ns', ns.replace('.', '_')]), ns, 'absolute']) + + return order, definitions + else: + order = [''.join(['dns_', ns.replace('.', '_')]) for ns in server_list] + definitions = dict() + for ns in server_list: + definitions[''.join(['dns_', ns.replace('.', '_')])] = {'options': [None, 'DNS Response Time', 'ms', ns, + 'dns_query_time.response_time', 'area'], + 'lines': [['_'.join(['ns', ns.replace('.', '_')]), + ns, 'absolute']]} + return order, definitions diff --git a/python.d/elasticsearch.chart.py b/python.d/elasticsearch.chart.py index 430227f69..9ec08719c 100644 --- a/python.d/elasticsearch.chart.py +++ b/python.d/elasticsearch.chart.py @@ -85,6 +85,21 @@ HEALTH_STATS = [ ('active_shards_percent_as_number', 'health_active_shards_percent_as_number', None) ] +LATENCY = { + 'query_latency': + {'total': 'query_total', + 'spent_time': 'query_time_in_millis'}, + 'fetch_latency': + {'total': 'fetch_total', + 'spent_time': 'fetch_time_in_millis'}, + 'indexing_latency': + {'total': 'indexing_index_total', + 'spent_time': 'indexing_index_time_in_millis'}, + 'flushing_latency': + {'total': 'flush_total', + 'spent_time': 'flush_total_time_in_millis'} +} + # charts order (can be overridden if you want less charts, or different order) ORDER = ['search_perf_total', 'search_perf_current', 'search_perf_time', 'search_latency', 'index_perf_total', 'index_perf_current', 'index_perf_time', 'index_latency', 'jvm_mem_heap', 'jvm_gc_count', @@ -95,34 +110,34 @@ ORDER = ['search_perf_total', 'search_perf_current', 'search_perf_time', 'search CHARTS = { 'search_perf_total': { - 'options': [None, 'Total number of queries, fetches', 'number of', 'search performance', + 'options': [None, 'Queries And Fetches', 'number of', 'search performance', 'es.search_query_total', 'stacked'], 'lines': [ ['query_total', 'queries', 'incremental'], ['fetch_total', 'fetches', 'incremental'] ]}, 'search_perf_current': { - 'options': [None, 'Number of queries, fetches in progress', 'number of', 'search performance', + 'options': [None, 'Queries and Fetches In Progress', 'number of', 'search performance', 'es.search_query_current', 'stacked'], 'lines': [ ['query_current', 'queries', 'absolute'], ['fetch_current', 'fetches', 'absolute'] ]}, 'search_perf_time': { - 'options': [None, 'Time spent on queries, fetches', 'seconds', 'search performance', + 'options': [None, 'Time Spent On Queries And Fetches', 'seconds', 'search performance', 'es.search_time', 'stacked'], 'lines': [ ['query_time_in_millis', 'query', 'incremental', 1, 1000], ['fetch_time_in_millis', 'fetch', 'incremental', 1, 1000] ]}, 'search_latency': { - 'options': [None, 'Query and fetch latency', 'ms', 'search performance', 'es.search_latency', 'stacked'], + 'options': [None, 'Query And Fetch Latency', 'ms', 'search performance', 'es.search_latency', 'stacked'], 'lines': [ ['query_latency', 'query', 'absolute', 1, 1000], ['fetch_latency', 'fetch', 'absolute', 1, 1000] ]}, 'index_perf_total': { - 'options': [None, 'Total number of documents indexed, index refreshes, index flushes to disk', 'number of', + 'options': [None, 'Indexed Documents, Index Refreshes, Index Flushes To Disk', 'number of', 'indexing performance', 'es.index_performance_total', 'stacked'], 'lines': [ ['indexing_index_total', 'indexed', 'incremental'], @@ -130,13 +145,13 @@ CHARTS = { ['flush_total', 'flushes', 'incremental'] ]}, 'index_perf_current': { - 'options': [None, 'Number of documents currently being indexed', 'currently indexed', + 'options': [None, 'Number Of Documents Currently Being Indexed', 'currently indexed', 'indexing performance', 'es.index_performance_current', 'stacked'], 'lines': [ ['indexing_index_current', 'documents', 'absolute'] ]}, 'index_perf_time': { - 'options': [None, 'Time spent on indexing, refreshing, flushing', 'seconds', 'indexing performance', + 'options': [None, 'Time Spent On Indexing, Refreshing, Flushing', 'seconds', 'indexing performance', 'es.search_time', 'stacked'], 'lines': [ ['indexing_index_time_in_millis', 'indexing', 'incremental', 1, 1000], @@ -144,33 +159,33 @@ CHARTS = { ['flush_total_time_in_millis', 'flushing', 'incremental', 1, 1000] ]}, 'index_latency': { - 'options': [None, 'Indexing and flushing latency', 'ms', 'indexing performance', + 'options': [None, 'Indexing And Flushing Latency', 'ms', 'indexing performance', 'es.index_latency', 'stacked'], 'lines': [ ['indexing_latency', 'indexing', 'absolute', 1, 1000], ['flushing_latency', 'flushing', 'absolute', 1, 1000] ]}, 'jvm_mem_heap': { - 'options': [None, 'JVM heap currently in use/committed', 'percent/MB', 'memory usage and gc', + 'options': [None, 'JVM Heap Currently in Use/Committed', 'percent/MB', 'memory usage and gc', 'es.jvm_heap', 'area'], 'lines': [ ['jvm_heap_percent', 'inuse', 'absolute'], ['jvm_heap_commit', 'commit', 'absolute', -1, 1048576] ]}, 'jvm_gc_count': { - 'options': [None, 'Count of garbage collections', 'counts', 'memory usage and gc', 'es.gc_count', 'stacked'], + 'options': [None, 'Garbage Collections', 'counts', 'memory usage and gc', 'es.gc_count', 'stacked'], 'lines': [ ['young_collection_count', 'young', 'incremental'], ['old_collection_count', 'old', 'incremental'] ]}, 'jvm_gc_time': { - 'options': [None, 'Time spent on garbage collections', 'ms', 'memory usage and gc', 'es.gc_time', 'stacked'], + 'options': [None, 'Time Spent On Garbage Collections', 'ms', 'memory usage and gc', 'es.gc_time', 'stacked'], 'lines': [ ['young_collection_time_in_millis', 'young', 'incremental'], ['old_collection_time_in_millis', 'old', 'incremental'] ]}, 'thread_pool_qr_q': { - 'options': [None, 'Number of queued threads in thread pool', 'queued threads', 'queues and rejections', + 'options': [None, 'Number Of Queued Threads In Thread Pool', 'queued threads', 'queues and rejections', 'es.thread_pool_queued', 'stacked'], 'lines': [ ['bulk_queue', 'bulk', 'absolute'], @@ -179,7 +194,7 @@ CHARTS = { ['merge_queue', 'merge', 'absolute'] ]}, 'thread_pool_qr_r': { - 'options': [None, 'Number of rejected threads in thread pool', 'rejected threads', 'queues and rejections', + 'options': [None, 'Rejected Threads In Thread Pool', 'rejected threads', 'queues and rejections', 'es.thread_pool_rejected', 'stacked'], 'lines': [ ['bulk_rejected', 'bulk', 'absolute'], @@ -188,19 +203,19 @@ CHARTS = { ['merge_rejected', 'merge', 'absolute'] ]}, 'fdata_cache': { - 'options': [None, 'Fielddata cache size', 'MB', 'fielddata cache', 'es.fdata_cache', 'line'], + 'options': [None, 'Fielddata Cache', 'MB', 'fielddata cache', 'es.fdata_cache', 'line'], 'lines': [ ['index_fdata_memory', 'cache', 'absolute', 1, 1048576] ]}, 'fdata_ev_tr': { - 'options': [None, 'Fielddata evictions and circuit breaker tripped count', 'number of events', + 'options': [None, 'Fielddata Evictions And Circuit Breaker Tripped Count', 'number of events', 'fielddata cache', 'es.evictions_tripped', 'line'], 'lines': [ ['evictions', None, 'incremental'], ['tripped', None, 'incremental'] ]}, 'cluster_health_nodes': { - 'options': [None, 'Nodes and tasks statistics', 'units', 'cluster health API', + 'options': [None, 'Nodes And Tasks Statistics', 'units', 'cluster health API', 'es.cluster_health_nodes', 'stacked'], 'lines': [ ['health_number_of_nodes', 'nodes', 'absolute'], @@ -209,7 +224,7 @@ CHARTS = { ['health_number_of_in_flight_fetch', 'in_flight_fetch', 'absolute'] ]}, 'cluster_health_status': { - 'options': [None, 'Cluster status', 'status', 'cluster health API', + 'options': [None, 'Cluster Status', 'status', 'cluster health API', 'es.cluster_health_status', 'area'], 'lines': [ ['status_green', 'green', 'absolute'], @@ -220,7 +235,7 @@ CHARTS = { ['status_yellow', 'yellow', 'absolute'] ]}, 'cluster_health_shards': { - 'options': [None, 'Shards statistics', 'shards', 'cluster health API', + 'options': [None, 'Shards Statistics', 'shards', 'cluster health API', 'es.cluster_health_shards', 'stacked'], 'lines': [ ['health_active_shards', 'active_shards', 'absolute'], @@ -231,7 +246,7 @@ CHARTS = { ['health_active_shards_percent_as_number', 'active_percent', 'absolute'] ]}, 'cluster_stats_nodes': { - 'options': [None, 'Nodes statistics', 'nodes', 'cluster stats API', + 'options': [None, 'Nodes Statistics', 'nodes', 'cluster stats API', 'es.cluster_nodes', 'stacked'], 'lines': [ ['count_data_only', 'data_only', 'absolute'], @@ -241,46 +256,46 @@ CHARTS = { ['count_client', 'client', 'absolute'] ]}, 'cluster_stats_query_cache': { - 'options': [None, 'Query cache statistics', 'queries', 'cluster stats API', + 'options': [None, 'Query Cache Statistics', 'queries', 'cluster stats API', 'es.cluster_query_cache', 'stacked'], 'lines': [ ['query_cache_hit_count', 'hit', 'incremental'], ['query_cache_miss_count', 'miss', 'incremental'] ]}, 'cluster_stats_docs': { - 'options': [None, 'Docs statistics', 'count', 'cluster stats API', + 'options': [None, 'Docs Statistics', 'count', 'cluster stats API', 'es.cluster_docs', 'line'], 'lines': [ ['docs_count', 'docs', 'absolute'] ]}, 'cluster_stats_store': { - 'options': [None, 'Store statistics', 'MB', 'cluster stats API', + 'options': [None, 'Store Statistics', 'MB', 'cluster stats API', 'es.cluster_store', 'line'], 'lines': [ ['store_size_in_bytes', 'size', 'absolute', 1, 1048567] ]}, 'cluster_stats_indices_shards': { - 'options': [None, 'Indices and shards statistics', 'count', 'cluster stats API', + 'options': [None, 'Indices And Shards Statistics', 'count', 'cluster stats API', 'es.cluster_indices_shards', 'stacked'], 'lines': [ ['indices_count', 'indices', 'absolute'], ['shards_total', 'shards', 'absolute'] ]}, 'host_metrics_transport': { - 'options': [None, 'Cluster communication transport metrics', 'kbit/s', 'host metrics', + 'options': [None, 'Cluster Communication Transport Metrics', 'kilobit/s', 'host metrics', 'es.host_transport', 'area'], 'lines': [ ['transport_rx_size_in_bytes', 'in', 'incremental', 8, 1000], ['transport_tx_size_in_bytes', 'out', 'incremental', -8, 1000] ]}, 'host_metrics_file_descriptors': { - 'options': [None, 'Available file descriptors in percent', 'percent', 'host metrics', + 'options': [None, 'Available File Descriptors In Percent', 'percent', 'host metrics', 'es.host_descriptors', 'area'], 'lines': [ ['file_descriptors_used', 'used', 'absolute', 1, 10] ]}, 'host_metrics_http': { - 'options': [None, 'Opened HTTP connections', 'connections', 'host metrics', + 'options': [None, 'Opened HTTP Connections', 'connections', 'host metrics', 'es.host_http_connections', 'line'], 'lines': [ ['http_current_open', 'opened', 'absolute', 1, 1] @@ -300,12 +315,13 @@ class Service(UrlService): self.methods = list() def check(self): - # We can't start if AND not specified - if not all([self.host, self.port, isinstance(self.host, str), isinstance(self.port, (str, int))]): + if not all([self.host, + self.port, + isinstance(self.host, str), + isinstance(self.port, (str, int))]): self.error('Host is not defined in the module configuration file') return False - # It as a bad idea to use hostname. # Hostname -> ip address try: self.host = gethostbyname(self.host) @@ -313,45 +329,33 @@ class Service(UrlService): self.error(str(error)) return False - scheme = 'http' if self.scheme else 'https' + scheme = 'http' if self.scheme == 'http' else 'https' # Add handlers (auth, self signed cert accept) self.url = '%s://%s:%s' % (scheme, self.host, self.port) - self._UrlService__add_openers() + self.opener = self._build_opener() # Create URL for every Elasticsearch API url_node_stats = '%s://%s:%s/_nodes/_local/stats' % (scheme, self.host, self.port) url_cluster_health = '%s://%s:%s/_cluster/health' % (scheme, self.host, self.port) url_cluster_stats = '%s://%s:%s/_cluster/stats' % (scheme, self.host, self.port) - # Create list of enabled API calls user_choice = [bool(self.configuration.get('node_stats', True)), bool(self.configuration.get('cluster_health', True)), bool(self.configuration.get('cluster_stats', True))] - avail_methods = [METHODS(get_data_function=self._get_node_stats_, url=url_node_stats), - METHODS(get_data_function=self._get_cluster_health_, url=url_cluster_health), - METHODS(get_data_function=self._get_cluster_stats_, url=url_cluster_stats)] + avail_methods = [METHODS(get_data_function=self._get_node_stats_, + url=url_node_stats), + METHODS(get_data_function=self._get_cluster_health_, + url=url_cluster_health), + METHODS(get_data_function=self._get_cluster_stats_, + url=url_cluster_stats)] # Remove disabled API calls from 'avail methods' self.methods = [avail_methods[e[0]] for e in enumerate(avail_methods) if user_choice[e[0]]] - - # Run _get_data for ALL active API calls. - api_check_result = dict() - data_from_check = dict() - for method in self.methods: - try: - api_check_result[method.url] = method.get_data_function(None, method.url) - data_from_check.update(api_check_result[method.url] or dict()) - except KeyError as error: - self.error('Failed to parse %s. Error: %s' % (method.url, str(error))) - return False - - # We can start ONLY if all active API calls returned NOT None - if not all(api_check_result.values()): - self.error('Plugin could not get data from all APIs') + data = self._get_data() + if not data: return False - else: - self._data_from_check = data_from_check - return True + self._data_from_check = data + return True def _get_data(self): threads = list() @@ -359,7 +363,8 @@ class Service(UrlService): result = dict() for method in self.methods: - th = Thread(target=method.get_data_function, args=(queue, method.url)) + th = Thread(target=method.get_data_function, + args=(queue, method.url)) th.start() threads.append(th) @@ -378,18 +383,18 @@ class Service(UrlService): raw_data = self._get_raw_data(url) if not raw_data: - return queue.put(dict()) if queue else None - else: - data = loads(raw_data) + return queue.put(dict()) - to_netdata = fetch_data_(raw_data=data, metrics_list=HEALTH_STATS) + data = loads(raw_data) + to_netdata = fetch_data_(raw_data=data, + metrics_list=HEALTH_STATS) - to_netdata.update({'status_green': 0, 'status_red': 0, 'status_yellow': 0, - 'status_foo1': 0, 'status_foo2': 0, 'status_foo3': 0}) - current_status = 'status_' + data['status'] - to_netdata[current_status] = 1 + to_netdata.update({'status_green': 0, 'status_red': 0, 'status_yellow': 0, + 'status_foo1': 0, 'status_foo2': 0, 'status_foo3': 0}) + current_status = 'status_' + data['status'] + to_netdata[current_status] = 1 - return queue.put(to_netdata) if queue else to_netdata + return queue.put(to_netdata) def _get_cluster_stats_(self, queue, url): """ @@ -400,13 +405,13 @@ class Service(UrlService): raw_data = self._get_raw_data(url) if not raw_data: - return queue.put(dict()) if queue else None - else: - data = loads(raw_data) + return queue.put(dict()) - to_netdata = fetch_data_(raw_data=data, metrics_list=CLUSTER_STATS) + data = loads(raw_data) + to_netdata = fetch_data_(raw_data=data, + metrics_list=CLUSTER_STATS) - return queue.put(to_netdata) if queue else to_netdata + return queue.put(to_netdata) def _get_node_stats_(self, queue, url): """ @@ -417,47 +422,46 @@ class Service(UrlService): raw_data = self._get_raw_data(url) if not raw_data: - return queue.put(dict()) if queue else None - else: - data = loads(raw_data) - - node = list(data['nodes'].keys())[0] - to_netdata = fetch_data_(raw_data=data['nodes'][node], metrics_list=NODE_STATS) + return queue.put(dict()) - # Search performance latency - to_netdata['query_latency'] = self.find_avg_(to_netdata['query_total'], - to_netdata['query_time_in_millis'], 'query_latency') - to_netdata['fetch_latency'] = self.find_avg_(to_netdata['fetch_total'], - to_netdata['fetch_time_in_millis'], 'fetch_latency') + data = loads(raw_data) - # Indexing performance latency - to_netdata['indexing_latency'] = self.find_avg_(to_netdata['indexing_index_total'], - to_netdata['indexing_index_time_in_millis'], 'index_latency') - to_netdata['flushing_latency'] = self.find_avg_(to_netdata['flush_total'], - to_netdata['flush_total_time_in_millis'], 'flush_latency') + node = list(data['nodes'].keys())[0] + to_netdata = fetch_data_(raw_data=data['nodes'][node], + metrics_list=NODE_STATS) + # Search, index, flush, fetch performance latency + for key in LATENCY: + try: + to_netdata[key] = self.find_avg_(total=to_netdata[LATENCY[key]['total']], + spent_time=to_netdata[LATENCY[key]['spent_time']], + key=key) + except KeyError: + continue + if 'open_file_descriptors' in to_netdata and 'max_file_descriptors' in to_netdata: to_netdata['file_descriptors_used'] = round(float(to_netdata['open_file_descriptors']) / to_netdata['max_file_descriptors'] * 1000) - return queue.put(to_netdata) if queue else to_netdata + return queue.put(to_netdata) - def find_avg_(self, value1, value2, key): + def find_avg_(self, total, spent_time, key): if key not in self.latency: - self.latency.update({key: [value1, value2]}) + self.latency[key] = dict(total=total, + spent_time=spent_time) return 0 - else: - if not self.latency[key][0] == value1: - latency = round(float(value2 - self.latency[key][1]) / float(value1 - self.latency[key][0]) * 1000) - self.latency.update({key: [value1, value2]}) - return latency - else: - self.latency.update({key: [value1, value2]}) - return 0 + if self.latency[key]['total'] != total: + latency = float(spent_time - self.latency[key]['spent_time'])\ + / float(total - self.latency[key]['total']) * 1000 + self.latency[key]['total'] = total + self.latency[key]['spent_time'] = spent_time + return latency + self.latency[key]['spent_time'] = spent_time + return 0 def fetch_data_(raw_data, metrics_list): to_netdata = dict() - for metric, new_name, function in metrics_list: + for metric, new_name, func in metrics_list: value = raw_data for key in metric.split('.'): try: @@ -465,7 +469,7 @@ def fetch_data_(raw_data, metrics_list): except KeyError: break if not isinstance(value, dict) and key: - to_netdata[new_name or key] = value if not function else function(value) + to_netdata[new_name or key] = value if not func else func(value) return to_netdata diff --git a/python.d/fail2ban.chart.py b/python.d/fail2ban.chart.py index c7d24e8c1..35761e894 100644 --- a/python.d/fail2ban.chart.py +++ b/python.d/fail2ban.chart.py @@ -2,131 +2,210 @@ # Description: fail2ban log netdata python.d module # Author: l2isbad -from base import LogService -from re import compile - -try: - from itertools import filterfalse -except ImportError: - from itertools import ifilterfalse as filterfalse +from re import compile as r_compile from os import access as is_accessible, R_OK -from os.path import isdir +from os.path import isdir, getsize from glob import glob +import bisect +from base import LogService priority = 60000 retries = 60 -REGEX = compile(r'\[([A-Za-z-_]+)][^\[\]]*?(?[A-Za-z-_0-9]+)\] (?P(?:(U|B)))[a-z]+ (?P\d{1,3}(?:\.\d{1,3}){3})') +ORDER = ['jails_bans', 'jails_in_jail'] class Service(LogService): + """ + fail2ban log class + Reads logs line by line + Jail auto detection included + It produces following charts: + * Bans per second for every jail + * Banned IPs for every jail (since the last restart of netdata) + """ def __init__(self, configuration=None, name=None): LogService.__init__(self, configuration=configuration, name=name) self.order = ORDER + self.definitions = dict() self.log_path = self.configuration.get('log_path', '/var/log/fail2ban.log') self.conf_path = self.configuration.get('conf_path', '/etc/fail2ban/jail.local') - self.conf_dir = self.configuration.get('conf_dir', '') - try: - self.exclude = self.configuration['exclude'].split() - except (KeyError, AttributeError): - self.exclude = [] + self.conf_dir = self.configuration.get('conf_dir', '/etc/fail2ban/jail.d/') + self.exclude = self.configuration.get('exclude') def _get_data(self): """ Parse new log lines :return: dict """ - try: - raw = self._get_raw_data() - if raw is None: - return None - elif not raw: - return self.data - except (ValueError, AttributeError): + raw = self._get_raw_data() + if raw is None: return None + elif not raw: + return self.to_netdata # Fail2ban logs looks like # 2016-12-25 12:36:04,711 fail2ban.actions[2455]: WARNING [ssh] Ban 178.156.32.231 - data = dict( - zip( - self.jails_list, - [len(list(filterfalse(lambda line: (jail + '] Ban') not in line, raw))) for jail in self.jails_list] - )) - - for jail in data: - self.data[jail] += data[jail] - - return self.data + for row in raw: + match = REGEX_DATA.search(row) + if match: + match_dict = match.groupdict() + jail, action, ipaddr = match_dict['jail'], match_dict['action'], match_dict['ipaddr'] + if jail in self.jails_list: + if action == 'B': + self.to_netdata[jail] += 1 + if address_not_in_jail(self.banned_ips[jail], ipaddr, self.to_netdata[jail + '_in_jail']): + self.to_netdata[jail + '_in_jail'] += 1 + else: + if ipaddr in self.banned_ips[jail]: + self.banned_ips[jail].remove(ipaddr) + self.to_netdata[jail + '_in_jail'] -= 1 + + return self.to_netdata def check(self): + """ + :return: bool - # Check "log_path" is accessible. - # If NOT STOP plugin - if not is_accessible(self.log_path, R_OK): - self.error('Cannot access file %s' % self.log_path) - return False - jails_list = list() - - if self.conf_dir: - dir_jails, error = parse_conf_dir(self.conf_dir) - jails_list.extend(dir_jails) - if not dir_jails: - self.error(error) - - if self.conf_path: - path_jails, error = parse_conf_path(self.conf_path) - jails_list.extend(path_jails) - if not path_jails: - self.error(error) + Check if the "log_path" is not empty and readable + """ - # If for some reason parse failed we still can START with default jails_list. - self.jails_list = list(set(jails_list) - set(self.exclude)) or ['ssh'] - self.data = dict([(jail, 0) for jail in self.jails_list]) - self.create_dimensions() - self.info('Plugin successfully started. Jails: %s' % self.jails_list) + if not (is_accessible(self.log_path, R_OK) and getsize(self.log_path) != 0): + self.error('%s is not readable or empty' % self.log_path) + return False + self.jails_list, self.to_netdata, self.banned_ips = self.jails_auto_detection_() + self.definitions = create_definitions_(self.jails_list) + self.info('Jails: %s' % self.jails_list) return True - def create_dimensions(self): - self.definitions = { - 'jails_group': {'options': [None, "Jails ban statistics", "bans/s", 'jails', 'jail.ban', 'line'], - 'lines': []}} - for jail in self.jails_list: - self.definitions['jails_group']['lines'].append([jail, jail, 'incremental']) - + def jails_auto_detection_(self): + """ + return: -def parse_conf_dir(conf_dir): - if not isdir(conf_dir): - return list(), '%s is not a directory' % conf_dir + * jails_list - list of enabled jails (['ssh', 'apache', ...]) + * to_netdata - dict ({'ssh': 0, 'ssh_in_jail': 0, ...}) + * banned_ips - here will be stored all the banned ips ({'ssh': ['1.2.3.4', '5.6.7.8', ...], ...}) + """ + raw_jails_list = list() + jails_list = list() - jail_local = list(filter(lambda local: is_accessible(local, R_OK), glob(conf_dir + '/*.local'))) - jail_conf = list(filter(lambda conf: is_accessible(conf, R_OK), glob(conf_dir + '/*.conf'))) + for raw_jail in parse_configuration_files_(self.conf_path, self.conf_dir, self.error): + raw_jails_list.extend(raw_jail) - if not (jail_local or jail_conf): - return list(), '%s is empty or not readable' % conf_dir + for jail, status in raw_jails_list: + if status == 'true' and jail not in jails_list: + jails_list.append(jail) + elif status == 'false' and jail in jails_list: + jails_list.remove(jail) - # According "man jail.conf" files could be *.local AND *.conf - # *.conf files parsed first. Changes in *.local overrides configuration in *.conf - if jail_conf: - jail_local.extend([conf for conf in jail_conf if conf[:-5] not in [local[:-6] for local in jail_local]]) + # If for some reason parse failed we still can START with default jails_list. + jails_list = list(set(jails_list) - set(self.exclude.split() + if isinstance(self.exclude, str) else list())) or ['ssh'] + + to_netdata = dict([(jail, 0) for jail in jails_list]) + to_netdata.update(dict([(jail + '_in_jail', 0) for jail in jails_list])) + banned_ips = dict([(jail, list()) for jail in jails_list]) + + return jails_list, to_netdata, banned_ips + + +def create_definitions_(jails_list): + """ + Chart definitions creating + """ + + definitions = { + 'jails_bans': {'options': [None, 'Jails Ban Statistics', 'bans/s', 'bans', 'jail.bans', 'line'], + 'lines': []}, + 'jails_in_jail': {'options': [None, 'Banned IPs (since the last restart of netdata)', 'IPs', + 'in jail', 'jail.in_jail', 'line'], + 'lines': []}} + for jail in jails_list: + definitions['jails_bans']['lines'].append([jail, jail, 'incremental']) + definitions['jails_in_jail']['lines'].append([jail + '_in_jail', jail, 'absolute']) + + return definitions + + +def parse_configuration_files_(jails_conf_path, jails_conf_dir, print_error): + """ + :param jails_conf_path: + :param jails_conf_dir: + :param print_error: + :return: + + Uses "find_jails_in_files" function to find all jails in the "jails_conf_dir" directory + and in the "jails_conf_path" + + All files must endswith ".local" or ".conf" + Return order is important. + According man jail.conf it should be + * jail.conf + * jail.d/*.conf (in alphabetical order) + * jail.local + * jail.d/*.local (in alphabetical order) + """ + path_conf, path_local, dir_conf, dir_local = list(), list(), list(), list() + + # Parse files in the directory + if not (isinstance(jails_conf_dir, str) and isdir(jails_conf_dir)): + print_error('%s is not a directory' % jails_conf_dir) + else: + dir_conf = list(filter(lambda conf: is_accessible(conf, R_OK), glob(jails_conf_dir + '/*.conf'))) + dir_local = list(filter(lambda local: is_accessible(local, R_OK), glob(jails_conf_dir + '/*.local'))) + if not (dir_conf or dir_local): + print_error('%s is empty or not readable' % jails_conf_dir) + else: + dir_conf, dir_local = (find_jails_in_files(dir_conf, print_error), + find_jails_in_files(dir_local, print_error)) + + # Parse .conf and .local files + if isinstance(jails_conf_path, str) and jails_conf_path.endswith(('.local', '.conf')): + path_conf, path_local = (find_jails_in_files([jails_conf_path.split('.')[0] + '.conf'], print_error), + find_jails_in_files([jails_conf_path.split('.')[0] + '.local'], print_error)) + + return path_conf, dir_conf, path_local, dir_local + + +def find_jails_in_files(list_of_files, print_error): + """ + :param list_of_files: + :param print_error: + :return: + + Open a file and parse it to find all (enabled and disabled) jails + The output is a list of tuples: + [('ssh', 'true'), ('apache', 'false'), ...] + """ jails_list = list() - for conf in jail_local: - with open(conf, 'rt') as f: - raw_data = f.read() - - data = ' '.join(raw_data.split()) - jails_list.extend(REGEX.findall(data)) - jails_list = list(set(jails_list)) - - return jails_list, 'can\'t locate any jails in %s. Default jail is [\'ssh\']' % conf_dir - - -def parse_conf_path(conf_path): - if not is_accessible(conf_path, R_OK): - return list(), '%s is not readable' % conf_path - - with open(conf_path, 'rt') as jails_conf: - raw_data = jails_conf.read() - - data = raw_data.split() - jails_list = REGEX.findall(' '.join(data)) - return jails_list, 'can\'t locate any jails in %s. Default jail is [\'ssh\']' % conf_path + for conf in list_of_files: + if is_accessible(conf, R_OK): + with open(conf, 'rt') as conf: + raw_data = conf.read() + data = ' '.join(raw_data.split()) + jails_list.extend(REGEX_JAILS.findall(data)) + else: + print_error('%s is not readable or not exist' % conf) + return jails_list + + +def address_not_in_jail(pool, address, pool_size): + """ + :param pool: + :param address: + :param pool_size: + :return: bool + + Checks if the address is in the pool. + If not address will be added + """ + index = bisect.bisect_left(pool, address) + if index < pool_size: + if pool[index] == address: + return False + bisect.insort_left(pool, address) + return True + else: + bisect.insort_left(pool, address) + return True diff --git a/python.d/go_expvar.chart.py b/python.d/go_expvar.chart.py new file mode 100644 index 000000000..e1a334cc3 --- /dev/null +++ b/python.d/go_expvar.chart.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +# Description: go_expvar netdata python.d module +# Author: Jan Kral (kralewitz) + +from __future__ import division +from base import UrlService +import json + +# default module values (can be overridden per job in `config`) +# update_every = 2 +priority = 60000 +retries = 60 + + +MEMSTATS_CHARTS = { + 'memstats_heap': { + 'options': ['heap', 'memory: size of heap memory structures', 'kB', 'memstats', 'expvar.memstats.heap', 'line'], + 'lines': [ + ['memstats_heap_alloc', 'alloc', 'absolute', 1, 1024], + ['memstats_heap_inuse', 'inuse', 'absolute', 1, 1024] + ]}, + 'memstats_stack': { + 'options': ['stack', 'memory: size of stack memory structures', 'kB', 'memstats', 'expvar.memstats.stack', 'line'], + 'lines': [ + ['memstats_stack_inuse', 'inuse', 'absolute', 1, 1024] + ]}, + 'memstats_mspan': { + 'options': ['mspan', 'memory: size of mspan memory structures', 'kB', 'memstats', 'expvar.memstats.mspan', 'line'], + 'lines': [ + ['memstats_mspan_inuse', 'inuse', 'absolute', 1, 1024] + ]}, + 'memstats_mcache': { + 'options': ['mcache', 'memory: size of mcache memory structures', 'kB', 'memstats', 'expvar.memstats.mcache', 'line'], + 'lines': [ + ['memstats_mcache_inuse', 'inuse', 'absolute', 1, 1024] + ]}, + 'memstats_live_objects': { + 'options': ['live_objects', 'memory: number of live objects', 'objects', 'memstats', 'expvar.memstats.live_objects', 'line'], + 'lines': [ + ['memstats_live_objects', 'live'] + ]}, + 'memstats_sys': { + 'options': ['sys', 'memory: size of reserved virtual address space', 'kB', 'memstats', 'expvar.memstats.sys', 'line'], + 'lines': [ + ['memstats_sys', 'sys', 'absolute', 1, 1024] + ]}, + 'memstats_gc_pauses': { + 'options': ['gc_pauses', 'memory: average duration of GC pauses', 'ns', 'memstats', 'expvar.memstats.gc_pauses', 'line'], + 'lines': [ + ['memstats_gc_pauses', 'avg'] + ]}, +} + +MEMSTATS_ORDER = ['memstats_heap', 'memstats_stack', 'memstats_mspan', 'memstats_mcache', 'memstats_sys', 'memstats_live_objects', 'memstats_gc_pauses'] + + +def flatten(d, top='', sep='.'): + items = [] + for key, val in d.items(): + nkey = top + sep + key if top else key + if isinstance(val, dict): + items.extend(flatten(val, nkey, sep=sep).items()) + else: + items.append((nkey, val)) + return dict(items) + + +class Service(UrlService): + def __init__(self, configuration=None, name=None): + UrlService.__init__(self, configuration=configuration, name=name) + + # if memstats collection is enabled, add the charts and their order + if self.configuration.get('collect_memstats'): + self.definitions = MEMSTATS_CHARTS + self.order = MEMSTATS_ORDER + else: + self.definitions = dict() + self.order = list() + + # if extra charts are defined, parse their config + extra_charts = self.configuration.get('extra_charts') + if extra_charts: + self._parse_extra_charts_config(extra_charts) + + def check(self): + """ + Check if the module can collect data: + 1) At least one JOB configuration has to be specified + 2) The JOB configuration needs to define the URL and either collect_memstats must be enabled or at least one + extra_chart must be defined. + + The configuration and URL check is provided by the UrlService class. + """ + + if not (self.configuration.get('extra_charts') or self.configuration.get('collect_memstats')): + self.error('Memstats collection is disabled and no extra_charts are defined, disabling module.') + return False + + return UrlService.check(self) + + def _parse_extra_charts_config(self, extra_charts_config): + + # a place to store the expvar keys and their types + self.expvars = dict() + + for chart in extra_charts_config: + + chart_dict = dict() + chart_id = chart.get('id') + chart_lines = chart.get('lines') + chart_opts = chart.get('options', dict()) + + if not all([chart_id, chart_lines]): + self.info('Chart {0} has no ID or no lines defined, skipping'.format(chart)) + continue + + chart_dict['options'] = [ + chart_opts.get('name', ''), + chart_opts.get('title', ''), + chart_opts.get('units', ''), + chart_opts.get('family', ''), + chart_opts.get('context', ''), + chart_opts.get('chart_type', 'line') + ] + chart_dict['lines'] = list() + + # add the lines to the chart + for line in chart_lines: + + ev_key = line.get('expvar_key') + ev_type = line.get('expvar_type') + line_id = line.get('id') + + if not all([ev_key, ev_type, line_id]): + self.info('Line missing expvar_key, expvar_type, or line_id, skipping: {0}'.format(line)) + continue + + if ev_type not in ['int', 'float']: + self.info('Unsupported expvar_type "{0}". Must be "int" or "float"'.format(ev_type)) + continue + + if ev_key in self.expvars: + self.info('Duplicate expvar key {0}: skipping line.'.format(ev_key)) + continue + + self.expvars[ev_key] = (ev_type, line_id) + + chart_dict['lines'].append( + [ + line.get('id', ''), + line.get('name', ''), + line.get('algorithm', ''), + line.get('multiplier', 1), + line.get('divisor', 100 if ev_type == 'float' else 1), + line.get('hidden', False) + ] + ) + + self.order.append(chart_id) + self.definitions[chart_id] = chart_dict + + def _get_data(self): + """ + Format data received from http request + :return: dict + """ + + raw_data = self._get_raw_data() + if not raw_data: + return None + + data = json.loads(raw_data) + + expvars = dict() + if self.configuration.get('collect_memstats'): + expvars.update(self._parse_memstats(data)) + + if self.configuration.get('extra_charts'): + # the memstats part of the data has been already parsed, so we remove it before flattening and checking + # the rest of the data, thus avoiding needless iterating over the multiply nested memstats dict. + del (data['memstats']) + flattened = flatten(data) + for k, v in flattened.items(): + ev = self.expvars.get(k) + if not ev: + # expvar is not defined in config, skip it + continue + try: + key_type, line_id = ev + if key_type == 'int': + expvars[line_id] = int(v) + elif key_type == 'float': + # if the value type is float, multiply it by 1000 and set line divisor to 1000 + expvars[line_id] = float(v) * 100 + except ValueError: + self.info('Failed to parse value for key {0} as {1}, ignoring key.'.format(k, key_type)) + del self.expvars[k] + + return expvars + + @staticmethod + def _parse_memstats(data): + + memstats = data['memstats'] + + # calculate the number of live objects in memory + live_objs = int(memstats['Mallocs']) - int(memstats['Frees']) + + # calculate GC pause times average + # the Go runtime keeps the last 256 GC pause durations in a circular buffer, + # so we need to filter out the 0 values before the buffer is filled + gc_pauses = memstats['PauseNs'] + try: + gc_pause_avg = sum(gc_pauses) / len([x for x in gc_pauses if x > 0]) + # no GC cycles have occured yet + except ZeroDivisionError: + gc_pause_avg = 0 + + return { + 'memstats_heap_alloc': memstats['HeapAlloc'], + 'memstats_heap_inuse': memstats['HeapInuse'], + 'memstats_stack_inuse': memstats['StackInuse'], + 'memstats_mspan_inuse': memstats['MSpanInuse'], + 'memstats_mcache_inuse': memstats['MCacheInuse'], + 'memstats_sys': memstats['Sys'], + 'memstats_live_objects': live_objs, + 'memstats_gc_pauses': gc_pause_avg, + } diff --git a/python.d/haproxy.chart.py b/python.d/haproxy.chart.py index 2fb97d755..67a6f7821 100644 --- a/python.d/haproxy.chart.py +++ b/python.d/haproxy.chart.py @@ -3,6 +3,13 @@ # Author: l2isbad from base import UrlService, SocketService +from collections import defaultdict +from re import compile as re_compile + +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse # default module values (can be overridden per job in `config`) # update_every = 2 @@ -10,135 +17,140 @@ priority = 60000 retries = 60 # charts order (can be overridden if you want less charts, or different order) -ORDER = ['fbin', 'fbout', 'fscur', 'fqcur', 'bbin', 'bbout', 'bscur', 'bqcur', 'health_sdown', 'health_bdown'] +ORDER = ['fbin', 'fbout', 'fscur', 'fqcur', 'bbin', 'bbout', 'bscur', 'bqcur', + 'health_sdown', 'health_bdown', 'health_idle'] CHARTS = { 'fbin': { - 'options': [None, "Kilobytes in", "kilobytes in/s", 'Frontend', 'haproxy_f.bin', 'line'], + 'options': [None, "Kilobytes In", "KB/s", 'frontend', 'haproxy_f.bin', 'line'], 'lines': [ ]}, 'fbout': { - 'options': [None, "Kilobytes out", "kilobytes out/s", 'Frontend', 'haproxy_f.bout', 'line'], + 'options': [None, "Kilobytes Out", "KB/s", 'frontend', 'haproxy_f.bout', 'line'], 'lines': [ ]}, 'fscur': { - 'options': [None, "Sessions active", "sessions", 'Frontend', 'haproxy_f.scur', 'line'], + 'options': [None, "Sessions Active", "sessions", 'frontend', 'haproxy_f.scur', 'line'], 'lines': [ ]}, 'fqcur': { - 'options': [None, "Session in queue", "sessions", 'Frontend', 'haproxy_f.qcur', 'line'], + 'options': [None, "Session In Queue", "sessions", 'frontend', 'haproxy_f.qcur', 'line'], 'lines': [ ]}, 'bbin': { - 'options': [None, "Kilobytes in", "kilobytes in/s", 'Backend', 'haproxy_b.bin', 'line'], + 'options': [None, "Kilobytes In", "KB/s", 'backend', 'haproxy_b.bin', 'line'], 'lines': [ ]}, 'bbout': { - 'options': [None, "Kilobytes out", "kilobytes out/s", 'Backend', 'haproxy_b.bout', 'line'], + 'options': [None, "Kilobytes Out", "KB/s", 'backend', 'haproxy_b.bout', 'line'], 'lines': [ ]}, 'bscur': { - 'options': [None, "Sessions active", "sessions", 'Backend', 'haproxy_b.scur', 'line'], + 'options': [None, "Sessions Active", "sessions", 'backend', 'haproxy_b.scur', 'line'], 'lines': [ ]}, 'bqcur': { - 'options': [None, "Sessions in queue", "sessions", 'Backend', 'haproxy_b.qcur', 'line'], + 'options': [None, "Sessions In Queue", "sessions", 'backend', 'haproxy_b.qcur', 'line'], 'lines': [ ]}, 'health_sdown': { - 'options': [None, "Number of servers in backend in DOWN state", "failed servers", 'Health', 'haproxy_hs.down', 'line'], + 'options': [None, "Backend Servers In DOWN State", "failed servers", 'health', + 'haproxy_hs.down', 'line'], 'lines': [ ]}, 'health_bdown': { - 'options': [None, "Is backend alive? 1 = DOWN", "failed backend", 'Health', 'haproxy_hb.down', 'line'], + 'options': [None, "Is Backend Alive? 1 = DOWN", "failed backend", 'health', 'haproxy_hb.down', 'line'], + 'lines': [ + ]}, + 'health_idle': { + 'options': [None, "The Ratio Of Polling Time Vs Total Time", "percent", 'health', 'haproxy.idle', 'line'], 'lines': [ + ['idle', None, 'absolute'] ]} } +METRICS = {'bin': {'algorithm': 'incremental', 'divisor': 1024}, + 'bout': {'algorithm': 'incremental', 'divisor': 1024}, + 'scur': {'algorithm': 'absolute', 'divisor': 1}, + 'qcur': {'algorithm': 'absolute', 'divisor': 1}} + +REGEX = dict(url=re_compile(r'idle = (?P[0-9]+)'), socket=re_compile(r'Idle_pct: (?P[0-9]+)')) + class Service(UrlService, SocketService): def __init__(self, configuration=None, name=None): - SocketService.__init__(self, configuration=configuration, name=name) - self.user = self.configuration.get('user') - self.password = self.configuration.get('pass') - self.request = 'show stat\n' - self.poll_method = (UrlService, SocketService) + if 'socket' in configuration: + SocketService.__init__(self, configuration=configuration, name=name) + self.poll = SocketService + self.options_ = dict(regex=REGEX['socket'], stat='show stat\n', info='show info\n') + else: + UrlService.__init__(self, configuration=configuration, name=name) + self.poll = UrlService + self.options_ = dict(regex=REGEX['url'], stat=self.url, info=url_remove_params(self.url)) self.order = ORDER - self.order_front = [_ for _ in ORDER if _.startswith('f')] - self.order_back = [_ for _ in ORDER if _.startswith('b')] self.definitions = CHARTS - self.charts = True def check(self): - if self.configuration.get('url'): - self.poll_method = self.poll_method[0] - url = self.configuration.get('url') - if not url.endswith(';csv;norefresh'): - self.error('Bad url(%s). Must be http://:/;csv;norefresh' % url) - return False - elif self.configuration.get('socket'): - self.poll_method = self.poll_method[1] - else: - self.error('No configuration is specified') - return False - - if self.poll_method.check(self): - self.info('Plugin was started succesfully. We are using %s.' % self.poll_method.__name__) + if self.poll.check(self): + self.create_charts() + self.info('We are using %s.' % self.poll.__name__) return True + return False - def create_charts(self, front_ends, back_ends): - for _ in range(len(front_ends)): - self.definitions['fbin']['lines'].append(['_'.join(['fbin', front_ends[_]['# pxname']]), front_ends[_]['# pxname'], 'incremental', 1, 1024]) - self.definitions['fbout']['lines'].append(['_'.join(['fbout', front_ends[_]['# pxname']]), front_ends[_]['# pxname'], 'incremental', 1, 1024]) - self.definitions['fscur']['lines'].append(['_'.join(['fscur', front_ends[_]['# pxname']]), front_ends[_]['# pxname'], 'absolute']) - self.definitions['fqcur']['lines'].append(['_'.join(['fqcur', front_ends[_]['# pxname']]), front_ends[_]['# pxname'], 'absolute']) - - for _ in range(len(back_ends)): - self.definitions['bbin']['lines'].append(['_'.join(['bbin', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'incremental', 1, 1024]) - self.definitions['bbout']['lines'].append(['_'.join(['bbout', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'incremental', 1, 1024]) - self.definitions['bscur']['lines'].append(['_'.join(['bscur', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'absolute']) - self.definitions['bqcur']['lines'].append(['_'.join(['bqcur', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'absolute']) - self.definitions['health_sdown']['lines'].append(['_'.join(['hsdown', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'absolute']) - self.definitions['health_bdown']['lines'].append(['_'.join(['hbdown', back_ends[_]['# pxname']]), back_ends[_]['# pxname'], 'absolute']) - def _get_data(self): - """ - Format data received from http request - :return: dict - """ - try: - raw_data = self.poll_method._get_raw_data(self).splitlines() - except Exception as e: - self.error(str(e)) - return None - - all_instances = [dict(zip(raw_data[0].split(','), raw_data[_].split(','))) for _ in range(1, len(raw_data))] - - back_ends = list(filter(is_backend, all_instances)) - front_ends = list(filter(is_frontend, all_instances)) - servers = list(filter(is_server, all_instances)) - - if self.charts: - self.create_charts(front_ends, back_ends) - self.charts = False - to_netdata = dict() + self.request, self.url = self.options_['stat'], self.options_['stat'] + stat_data = self._get_stat_data() + self.request, self.url = self.options_['info'], self.options_['info'] + info_data = self._get_info_data(regex=self.options_['regex']) - for frontend in front_ends: - for _ in self.order_front: - to_netdata.update({'_'.join([_, frontend['# pxname']]): int(frontend[_[1:]]) if frontend.get(_[1:]) else 0}) + to_netdata.update(stat_data) + to_netdata.update(info_data) + return to_netdata or None - for backend in back_ends: - for _ in self.order_back: - to_netdata.update({'_'.join([_, backend['# pxname']]): int(backend[_[1:]]) if backend.get(_[1:]) else 0}) - - for _ in range(len(back_ends)): - to_netdata.update({'_'.join(['hsdown', back_ends[_]['# pxname']]): - len([server for server in servers if is_server_down(server, back_ends, _)])}) - to_netdata.update({'_'.join(['hbdown', back_ends[_]['# pxname']]): 1 if is_backend_down(back_ends, _) else 0}) + def _get_stat_data(self): + """ + :return: dict + """ + raw_data = self.poll._get_raw_data(self) + + if not raw_data: + return dict() + + raw_data = raw_data.splitlines() + self.data = parse_data_([dict(zip(raw_data[0].split(','), raw_data[_].split(','))) + for _ in range(1, len(raw_data))]) + if not self.data: + return dict() + + stat_data = dict() + + for frontend in self.data['frontend']: + for metric in METRICS: + idx = frontend['# pxname'].replace('.', '_') + stat_data['_'.join(['frontend', metric, idx])] = frontend.get(metric) or 0 + + for backend in self.data['backend']: + name, idx = backend['# pxname'], backend['# pxname'].replace('.', '_') + stat_data['hsdown_' + idx] = len([server for server in self.data['servers'] + if server_down(server, name)]) + stat_data['hbdown_' + idx] = 1 if backend.get('status') == 'DOWN' else 0 + for metric in METRICS: + stat_data['_'.join(['backend', metric, idx])] = backend.get(metric) or 0 + return stat_data + + def _get_info_data(self, regex): + """ + :return: dict + """ + raw_data = self.poll._get_raw_data(self) + if not raw_data: + return dict() - return to_netdata + match = regex.search(raw_data) + return match.groupdict() if match else dict() - def _check_raw_data(self, data): + @staticmethod + def _check_raw_data(data): """ Check if all data has been gathered from socket :param data: str @@ -146,32 +158,54 @@ class Service(UrlService, SocketService): """ return not bool(data) -def is_backend(backend): - try: - return backend['svname'] == 'BACKEND' and backend['# pxname'] != 'stats' - except Exception: - return False - -def is_frontend(frontend): - try: - return frontend['svname'] == 'FRONTEND' and frontend['# pxname'] != 'stats' - except Exception: - return False - -def is_server(server): - try: - return not server['svname'].startswith(('FRONTEND', 'BACKEND')) - except Exception: - return False - -def is_server_down(server, back_ends, _): - try: - return server['# pxname'] == back_ends[_]['# pxname'] and server['status'] == 'DOWN' - except Exception: - return False - -def is_backend_down(back_ends, _): - try: - return back_ends[_]['status'] == 'DOWN' - except Exception: - return False + def create_charts(self): + for front in self.data['frontend']: + name, idx = front['# pxname'], front['# pxname'].replace('.', '_') + for metric in METRICS: + self.definitions['f' + metric]['lines'].append(['_'.join(['frontend', metric, idx]), + name, METRICS[metric]['algorithm'], 1, + METRICS[metric]['divisor']]) + for back in self.data['backend']: + name, idx = back['# pxname'], back['# pxname'].replace('.', '_') + for metric in METRICS: + self.definitions['b' + metric]['lines'].append(['_'.join(['backend', metric, idx]), + name, METRICS[metric]['algorithm'], 1, + METRICS[metric]['divisor']]) + self.definitions['health_sdown']['lines'].append(['hsdown_' + idx, name, 'absolute']) + self.definitions['health_bdown']['lines'].append(['hbdown_' + idx, name, 'absolute']) + + +def parse_data_(data): + def is_backend(backend): + return backend.get('svname') == 'BACKEND' and backend.get('# pxname') != 'stats' + + def is_frontend(frontend): + return frontend.get('svname') == 'FRONTEND' and frontend.get('# pxname') != 'stats' + + def is_server(server): + return not server.get('svname', '').startswith(('FRONTEND', 'BACKEND')) + + if not data: + return None + + result = defaultdict(list) + for elem in data: + if is_backend(elem): + result['backend'].append(elem) + continue + elif is_frontend(elem): + result['frontend'].append(elem) + continue + elif is_server(elem): + result['servers'].append(elem) + + return result or None + + +def server_down(server, backend_name): + return server.get('# pxname') == backend_name and server.get('status') == 'DOWN' + + +def url_remove_params(url): + parsed = urlparse(url or str()) + return '%s://%s%s' % (parsed.scheme, parsed.netloc, parsed.path) diff --git a/python.d/isc_dhcpd.chart.py b/python.d/isc_dhcpd.chart.py index bb9ba5cbc..a437f803b 100644 --- a/python.d/isc_dhcpd.chart.py +++ b/python.d/isc_dhcpd.chart.py @@ -2,30 +2,56 @@ # Description: isc dhcpd lease netdata python.d module # Author: l2isbad -from base import SimpleService from time import mktime, strptime, gmtime, time -from os import stat +from os import stat, access, R_OK +from os.path import isfile try: - from ipaddress import IPv4Address as ipaddress - from ipaddress import ip_network + from ipaddress import ip_network, ip_address have_ipaddress = True except ImportError: have_ipaddress = False try: - from itertools import filterfalse as filterfalse + from itertools import filterfalse except ImportError: from itertools import ifilterfalse as filterfalse - +from base import SimpleService priority = 60000 retries = 60 -update_every = 60 +update_every = 5 + +ORDER = ['pools_utilization', 'pools_active_leases', 'leases_total', 'parse_time', 'leases_size'] + +CHARTS = { + 'pools_utilization': { + 'options': [None, 'Pools Utilization', 'used in percent', 'utilization', + 'isc_dhcpd.utilization', 'line'], + 'lines': []}, + 'pools_active_leases': { + 'options': [None, 'Active Leases', 'leases per pool', 'active leases', + 'isc_dhcpd.active_leases', 'line'], + 'lines': []}, + 'leases_total': { + 'options': [None, 'Total All Pools', 'number', 'active leases', + 'isc_dhcpd.leases_total', 'line'], + 'lines': [['leases_total', 'leases', 'absolute']]}, + 'parse_time': { + 'options': [None, 'Parse Time', 'ms', 'parse stats', + 'isc_dhcpd.parse_time', 'line'], + 'lines': [['parse_time', 'time', 'absolute']]}, + 'leases_size': { + 'options': [None, 'Dhcpd Leases File Size', 'kilobytes', + 'parse stats', 'isc_dhcpd.leases_size', 'line'], + 'lines': [['leases_size', 'size', 'absolute', 1, 1024]]}} + class Service(SimpleService): def __init__(self, configuration=None, name=None): SimpleService.__init__(self, configuration=configuration, name=name) self.leases_path = self.configuration.get('leases_path', '/var/lib/dhcp/dhcpd.leases') - self.pools = self.configuration.get('pools') + self.order = ORDER + self.definitions = CHARTS + self.pools = dict() # Will work only with 'default' db-time-format (weekday year/month/day hour:minute:second) # TODO: update algorithm to parse correctly 'local' db-time-format @@ -33,50 +59,30 @@ class Service(SimpleService): # Also only ipv4 supported def check(self): - if not self._get_raw_data(): + if not have_ipaddress: + self.error('\'python-ipaddress\' module is needed') + return False + if not (isfile(self.leases_path) and access(self.leases_path, R_OK)): self.error('Make sure leases_path is correct and leases log file is readable by netdata') return False - elif not have_ipaddress: - self.error('No ipaddress module. Please install (py2-ipaddress in case of python2)') + if not self.configuration.get('pools'): + self.error('Pools are not defined') return False - else: + if not isinstance(self.configuration['pools'], dict): + self.error('Invalid \'pools\' format') + return False + + for pool in self.configuration['pools']: try: - self.pools = self.pools.split() - if not [ip_network(return_utf(pool)) for pool in self.pools]: - self.error('Pools list is empty') - return False - except (ValueError, IndexError, AttributeError, SyntaxError) as e: - self.error('Pools configurations is incorrect', str(e)) - return False - - # Creating static charts - self.order = ['parse_time', 'leases_size', 'utilization', 'total'] - self.definitions = {'utilization': - {'options': - [None, 'Pools utilization', 'used %', 'utilization', 'isc_dhcpd.util', 'line'], - 'lines': []}, - 'total': - {'options': - [None, 'Total all pools', 'leases', 'utilization', 'isc_dhcpd.total', 'line'], - 'lines': [['total', 'leases', 'absolute']]}, - 'parse_time': - {'options': - [None, 'Parse time', 'ms', 'parse stats', 'isc_dhcpd.parse', 'line'], - 'lines': [['ptime', 'time', 'absolute']]}, - 'leases_size': - {'options': - [None, 'dhcpd.leases file size', 'kilobytes', 'parse stats', 'isc_dhcpd.lsize', 'line'], - 'lines': [['lsize', 'size', 'absolute']]}} - # Creating dynamic charts - for pool in self.pools: - self.definitions['utilization']['lines'].append([''.join(['ut_', pool]), pool, 'absolute']) - self.order.append(''.join(['leases_', pool])) - self.definitions[''.join(['leases_', pool])] = \ - {'options': [None, 'Active leases', 'leases', 'pools', 'isc_dhcpd.lease', 'area'], - 'lines': [[''.join(['le_', pool]), pool, 'absolute']]} - - self.info('Plugin was started succesfully') - return True + net = ip_network(u'%s' % self.configuration['pools'][pool]) + self.pools[pool] = dict(net=net, num_hosts=net.num_addresses - 2) + except ValueError as error: + self.error('%s removed, error: %s' % (self.configuration['pools'][pool], error)) + + if not self.pools: + return False + self.create_charts() + return True def _get_raw_data(self): """ @@ -87,65 +93,60 @@ class Service(SimpleService): ) """ try: - with open(self.leases_path, 'rt') as dhcp_leases: + with open(self.leases_path) as leases: time_start = time() - part1 = filterfalse(find_lease, dhcp_leases) - part2 = filterfalse(find_ends, dhcp_leases) - raw_result = dict(zip(part1, part2)) + part1 = filterfalse(find_lease, leases) + part2 = filterfalse(find_ends, leases) + result = dict(zip(part1, part2)) time_end = time() file_parse_time = round((time_end - time_start) * 1000) - except Exception as e: - self.error("Failed to parse leases file:", str(e)) + return result, file_parse_time + except (OSError, IOError) as error: + self.error("Failed to parse leases file:", str(error)) return None - else: - result = (raw_result, file_parse_time) - return result def _get_data(self): """ :return: dict """ - raw_leases = self._get_raw_data() - if not raw_leases: + raw_data = self._get_raw_data() + if not raw_data: return None - # Result: {ipaddress: end lease time, ...} - all_leases = dict([(k[6:len(k)-3], v[7:len(v)-2]) for k, v in raw_leases[0].items()]) - - # Result: [active binding, active binding....]. (Expire time (ends date;) - current time > 0) - active_leases = [k for k, v in all_leases.items() if is_binding_active(all_leases[k])] - - # Result: {pool: number of active bindings in pool, ...} - pools_count = dict([(pool, len([lease for lease in active_leases if is_address_in(lease, pool)])) - for pool in self.pools]) - - # Result: {pool: number of host ip addresses in pool, ...} - pools_max = dict([(pool, (2 ** (32 - int(pool.split('/')[1])) - 2)) - for pool in self.pools]) - - # Result: {pool: % utilization, ....} (percent) - pools_util = dict([(pool, int(round(float(pools_count[pool]) / pools_max[pool] * 100, 0))) - for pool in self.pools]) - - # Bulding dicts to send to netdata - final_count = dict([(''.join(['le_', k]), v) for k, v in pools_count.items()]) - final_util = dict([(''.join(['ut_', k]), v) for k, v in pools_util.items()]) - - to_netdata = {'total': len(active_leases)} - to_netdata.update({'lsize': int(stat(self.leases_path)[6] / 1024)}) - to_netdata.update({'ptime': int(raw_leases[1])}) - to_netdata.update(final_util) - to_netdata.update(final_count) + raw_leases, parse_time = raw_data[0], raw_data[1] + # Result: {ipaddress: end lease time, ...} + active_leases, to_netdata = list(), dict() + current_time = mktime(gmtime()) + + for ip, lease_end_time in raw_leases.items(): + # Result: [active binding, active binding....]. (Expire time (ends date;) - current time > 0) + if binding_active(lease_end_time=lease_end_time[7:-2], + current_time=current_time): + active_leases.append(ip_address(u'%s' % ip[6:-3])) + + for pool in self.pools: + dim_id = pool.replace('.', '_') + pool_leases_count = len([ip for ip in active_leases if ip in self.pools[pool]['net']]) + to_netdata[dim_id + '_active_leases'] = pool_leases_count + to_netdata[dim_id + '_utilization'] = float(pool_leases_count) / self.pools[pool]['num_hosts'] * 10000 + + to_netdata['leases_total'] = len(active_leases) + to_netdata['leases_size'] = stat(self.leases_path)[6] + to_netdata['parse_time'] = parse_time return to_netdata + def create_charts(self): + for pool in self.pools: + dim, dim_id = pool, pool.replace('.', '_') + self.definitions['pools_utilization']['lines'].append([dim_id + '_utilization', + dim, 'absolute', 1, 100]) + self.definitions['pools_active_leases']['lines'].append([dim_id + '_active_leases', + dim, 'absolute']) -def is_binding_active(binding): - return mktime(strptime(binding, '%w %Y/%m/%d %H:%M:%S')) - mktime(gmtime()) > 0 - -def is_address_in(address, pool): - return ipaddress(return_utf(address)) in ip_network(return_utf(pool)) +def binding_active(lease_end_time, current_time): + return mktime(strptime(lease_end_time, '%w %Y/%m/%d %H:%M:%S')) - current_time > 0 def find_lease(value): @@ -155,10 +156,3 @@ def find_lease(value): def find_ends(value): return value[2:6] != 'ends' - -def return_utf(s): - # python2 returns "" for simple strings - # python3 returns "" for unicode strings - if str(type(s)) == "": - return unicode(s, 'utf-8') - return s diff --git a/python.d/mdstat.chart.py b/python.d/mdstat.chart.py index 0f7d2b444..ca9aba564 100644 --- a/python.d/mdstat.chart.py +++ b/python.d/mdstat.chart.py @@ -2,98 +2,125 @@ # Description: mdstat netdata python.d module # Author: l2isbad +from re import compile as re_compile +from collections import defaultdict from base import SimpleService -from re import compile + priority = 60000 retries = 60 update_every = 1 +OPERATIONS = ('check', 'resync', 'reshape', 'recovery', 'finish', 'speed') + class Service(SimpleService): def __init__(self, configuration=None, name=None): SimpleService.__init__(self, configuration=configuration, name=name) - self.order = ['agr_health'] - self.definitions = {'agr_health': - {'options': - [None, 'Faulty devices in MD', 'failed disks', 'health', 'md.health', 'line'], - 'lines': []}} - self.proc_mdstat = '/proc/mdstat' - self.regex_disks = compile(r'((?<=\ )[a-zA-Z_0-9]+(?= : active)).*?((?<= \[)[0-9]+)/([0-9]+(?=\] ))') - self.regex_status = compile(r'([a-zA-Z_0-9]+)( : active)[^:]*?([a-z]+) = ([0-9.]+(?=%)).*?((?<=finish=)[0-9.]+)min speed=([0-9]+)') + self.regex = dict(disks=re_compile(r' (?P[a-zA-Z_0-9]+) : active .+\[' + r'(?P[0-9]+)/' + r'(?P[0-9])\]'), + status=re_compile(r' (?P[a-zA-Z_0-9]+) : active .+ ' + r'(?P[a-z]+) = ' + r'(?P[0-9.]+).+finish=' + r'(?P([0-9.]+))min speed=' + r'(?P[0-9]+)')) def check(self): - raw_data = self._get_raw_data() - if not raw_data: - self.error('Cant read mdstat data from %s' % (self.proc_mdstat)) - return False - - md_list = [md[0] for md in self.regex_disks.findall(raw_data)] - - if not md_list: - self.error('No active arrays in %s' % (self.proc_mdstat)) - return False - else: - for md in md_list: - self.order.append(md) - self.order.append(''.join([md, '_status'])) - self.order.append(''.join([md, '_rate'])) - self.definitions['agr_health']['lines'].append([''.join([md, '_health']), md, 'absolute']) - self.definitions[md] = {'options': - [None, '%s disks stats' % md, 'disks', md, 'md.disks', 'stacked'], - 'lines': [[''.join([md, '_total']), 'total', 'absolute'], - [''.join([md, '_inuse']), 'inuse', 'absolute']]} - self.definitions[''.join([md, '_status'])] = {'options': - [None, '%s current status' % md, 'percent', md, 'md.status', 'line'], - 'lines': [[''.join([md, '_resync']), 'resync', 'absolute', 1, 100], - [''.join([md, '_recovery']), 'recovery', 'absolute', 1, 100], - [''.join([md, '_reshape']), 'reshape', 'absolute', 1, 100], - [''.join([md, '_check']), 'check', 'absolute', 1, 100]]} - self.definitions[''.join([md, '_rate'])] = {'options': - [None, '%s operation status' % md, 'rate', md, 'md.rate', 'line'], - 'lines': [[''.join([md, '_finishin']), 'finish min', 'absolute', 1, 100], - [''.join([md, '_rate']), 'megabyte/s', 'absolute', -1, 100]]} - self.info('Plugin was started successfully. MDs to monitor %s' % (md_list)) - - return True - - def _get_raw_data(self): + arrays = find_arrays(self._get_raw_data(), self.regex) + if not arrays: + self.error('Failed to read data from /proc/mdstat or there is no active arrays') + return None + + self.order, self.definitions = create_charts(arrays.keys()) + return True + + @staticmethod + def _get_raw_data(): """ Read data from /proc/mdstat :return: str """ try: - with open(self.proc_mdstat, 'rt') as proc_mdstat: - raw_result = proc_mdstat.read() - except Exception: + with open('/proc/mdstat', 'rt') as proc_mdstat: + return proc_mdstat.readlines() or None + except (OSError, IOError): return None - else: - raw_result = ' '.join(raw_result.split()) - return raw_result def _get_data(self): """ Parse data from _get_raw_data() :return: dict """ - raw_mdstat = self._get_raw_data() - mdstat_disks = self.regex_disks.findall(raw_mdstat) - mdstat_status = self.regex_status.findall(raw_mdstat) - to_netdata = {} - - for md in mdstat_disks: - to_netdata[''.join([md[0], '_total'])] = int(md[1]) - to_netdata[''.join([md[0], '_inuse'])] = int(md[2]) - to_netdata[''.join([md[0], '_health'])] = int(md[1]) - int(md[2]) - to_netdata[''.join([md[0], '_check'])] = 0 - to_netdata[''.join([md[0], '_resync'])] = 0 - to_netdata[''.join([md[0], '_reshape'])] = 0 - to_netdata[''.join([md[0], '_recovery'])] = 0 - to_netdata[''.join([md[0], '_finishin'])] = 0 - to_netdata[''.join([md[0], '_rate'])] = 0 - - for md in mdstat_status: - to_netdata[''.join([md[0], '_' + md[2]])] = round(float(md[3]) * 100) - to_netdata[''.join([md[0], '_finishin'])] = round(float(md[4]) * 100) - to_netdata[''.join([md[0], '_rate'])] = round(float(md[5]) / 1000 * 100) + raw_data = self._get_raw_data() + arrays = find_arrays(raw_data, self.regex) + if not arrays: + return None + + to_netdata = dict() + for array, values in arrays.items(): + for key, value in values.items(): + to_netdata['_'.join([array, key])] = value return to_netdata + + +def find_arrays(raw_data, regex): + if raw_data is None: + return None + data = defaultdict(str) + counter = 1 + + for row in (elem.strip() for elem in raw_data): + if not row: + counter += 1 + continue + data[counter] = ' '.join([data[counter], row]) + + arrays = dict() + for value in data.values(): + match = regex['disks'].search(value) + if not match: + continue + + match = match.groupdict() + array = match.pop('array') + arrays[array] = match + arrays[array]['health'] = int(match['total_disks']) - int(match['inuse_disks']) + for operation in OPERATIONS: + arrays[array][operation] = 0 + + match = regex['status'].search(value) + if match: + match = match.groupdict() + if match['operation'] in OPERATIONS: + arrays[array][match['operation']] = float(match['operation_status']) * 100 + arrays[array]['finish'] = float(match['finish']) * 100 + arrays[array]['speed'] = float(match['speed']) / 1000 * 100 + + return arrays or None + + +def create_charts(arrays): + order = ['mdstat_health'] + definitions = dict(mdstat_health={'options': [None, 'Faulty devices in MD', 'failed disks', + 'health', 'md.health', 'line'], + 'lines': []}) + for md in arrays: + order.append(md) + order.append(md + '_status') + order.append(md + '_rate') + definitions['mdstat_health']['lines'].append([md + '_health', md, 'absolute']) + definitions[md] = {'options': [None, '%s disks stats' % md, 'disks', md, 'md.disks', 'stacked'], + 'lines': [[md + '_total_disks', 'total', 'absolute'], + [md + '_inuse_disks', 'inuse', 'absolute']]} + definitions[md + '_status'] = {'options': [None, '%s current status' % md, + 'percent', md, 'md.status', 'line'], + 'lines': [[md + '_resync', 'resync', 'absolute', 1, 100], + [md + '_recovery', 'recovery', 'absolute', 1, 100], + [md + '_reshape', 'reshape', 'absolute', 1, 100], + [md + '_check', 'check', 'absolute', 1, 100]]} + definitions[md + '_rate'] = {'options': [None, '%s operation status' % md, + 'rate', md, 'md.rate', 'line'], + 'lines': [[md + '_finish', 'finish min', 'absolute', 1, 100], + [md + '_speed', 'MB/s', 'absolute', -1, 100]]} + return order, definitions diff --git a/python.d/mongodb.chart.py b/python.d/mongodb.chart.py index c01bd293c..bb4c44b09 100644 --- a/python.d/mongodb.chart.py +++ b/python.d/mongodb.chart.py @@ -19,7 +19,7 @@ except ImportError: priority = 60000 retries = 60 -REPLSET_STATES = [ +REPL_SET_STATES = [ ('1', 'primary'), ('8', 'down'), ('2', 'secondary'), @@ -358,8 +358,8 @@ CHARTS = { 'options': [None, 'Lock on the oplog. Number of times the lock was acquired in the specified mode', 'locks', 'locks metrics', 'mongodb.locks_oplog', 'stacked'], 'lines': [ - ['Metadata_r', 'intent_shared', 'incremental'], - ['Metadata_w', 'intent_exclusive', 'incremental'] + ['oplog_r', 'intent_shared', 'incremental'], + ['oplog_w', 'intent_exclusive', 'incremental'] ]} } @@ -391,13 +391,16 @@ class Service(SimpleService): self.build_metrics_to_collect_(server_status) try: - self._get_data() + data = self._get_data() except (LookupError, SyntaxError, AttributeError): self.error('Type: %s, error: %s' % (str(exc_info()[0]), str(exc_info()[1]))) return False - else: + if isinstance(data, dict) and data: + self._data_from_check = data self.create_charts_(server_status) return True + self.error('_get_data() returned no data or type is not ') + return False def build_metrics_to_collect_(self, server_status): @@ -473,7 +476,7 @@ class Service(SimpleService): lines.append([dim_id, description, 'absolute', 1, 1]) return lines - all_hosts = server_status['repl']['hosts'] + all_hosts = server_status['repl']['hosts'] + server_status['repl'].get('arbiters', list()) this_host = server_status['repl']['me'] other_hosts = [host for host in all_hosts if host != this_host] @@ -503,19 +506,19 @@ class Service(SimpleService): self.definitions[chart_name] = { 'options': [None, 'Replica set member (%s) current state' % host, 'state', 'replication and oplog', 'mongodb.replication_state', 'line'], - 'lines': create_state_lines(REPLSET_STATES)} + 'lines': create_state_lines(REPL_SET_STATES)} def _get_raw_data(self): raw_data = dict() - raw_data.update(self.get_serverstatus_() or dict()) - raw_data.update(self.get_dbstats_() or dict()) - raw_data.update(self.get_replsetgetstatus_() or dict()) - raw_data.update(self.get_getreplicationinfo_() or dict()) + raw_data.update(self.get_server_status() or dict()) + raw_data.update(self.get_db_stats() or dict()) + raw_data.update(self.get_repl_set_get_status() or dict()) + raw_data.update(self.get_get_replication_info() or dict()) return raw_data or None - def get_serverstatus_(self): + def get_server_status(self): raw_data = dict() try: raw_data['serverStatus'] = self.connection.admin.command('serverStatus') @@ -524,7 +527,7 @@ class Service(SimpleService): else: return raw_data - def get_dbstats_(self): + def get_db_stats(self): if not self.databases: return None @@ -533,24 +536,22 @@ class Service(SimpleService): try: for dbase in self.databases: raw_data['dbStats'][dbase] = self.connection[dbase].command('dbStats') + return raw_data except PyMongoError: return None - else: - return raw_data - def get_replsetgetstatus_(self): + def get_repl_set_get_status(self): if not self.do_replica: return None raw_data = dict() try: raw_data['replSetGetStatus'] = self.connection.admin.command('replSetGetStatus') + return raw_data except PyMongoError: return None - else: - return raw_data - def get_getreplicationinfo_(self): + def get_get_replication_info(self): if not (self.do_replica and 'local' in self.databases): return None @@ -561,10 +562,9 @@ class Service(SimpleService): "$natural", ASCENDING).limit(1)[0] raw_data['getReplicationInfo']['DESCENDING'] = self.connection.local.oplog.rs.find().sort( "$natural", DESCENDING).limit(1)[0] + return raw_data except PyMongoError: return None - else: - return raw_data def _get_data(self): """ @@ -583,7 +583,7 @@ class Service(SimpleService): utc_now = datetime.utcnow() # serverStatus - for metric, new_name, function in self.metrics_to_collect: + for metric, new_name, func in self.metrics_to_collect: value = serverStatus for key in metric.split('.'): try: @@ -592,7 +592,7 @@ class Service(SimpleService): break if not isinstance(value, dict) and key: - to_netdata[new_name or key] = value if not function else function(value) + to_netdata[new_name or key] = value if not func else func(value) to_netdata['nonmapped'] = to_netdata['virtual'] - serverStatus['mem'].get('mappedWithJournal', to_netdata['mapped']) @@ -620,13 +620,13 @@ class Service(SimpleService): if not member.get('self'): other_hosts.append(member) # Replica set time diff between current time and time when last entry from the oplog was applied - if member['optimeDate'] != unix_epoch: + if member.get('optimeDate', unix_epoch) != unix_epoch: member_optimedate = member['name'] + '_optimedate' to_netdata.update({member_optimedate: int(delta_calculation(delta=utc_now - member['optimeDate'], multiplier=1000))}) # Replica set members state member_state = member['name'] + '_state' - for elem in REPLSET_STATES: + for elem in REPL_SET_STATES: state = elem[0] to_netdata.update({'_'.join([member_state, state]): 0}) to_netdata.update({'_'.join([member_state, str(member['state'])]): member['state']}) @@ -668,5 +668,4 @@ class Service(SimpleService): def delta_calculation(delta, multiplier=1): if hasattr(delta, 'total_seconds'): return delta.total_seconds() * multiplier - else: - return (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6) / 10.0 ** 6 * multiplier + return (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6) / 10.0 ** 6 * multiplier diff --git a/python.d/mysql.chart.py b/python.d/mysql.chart.py index abf6bf715..cdabe971d 100644 --- a/python.d/mysql.chart.py +++ b/python.d/mysql.chart.py @@ -117,7 +117,7 @@ GLOBAL_STATS = [ 'Connection_errors_tcpwrap'] def slave_seconds(value): - return value if value else -1 + return value if value is not '' else -1 def slave_running(value): return 1 if value == 'Yes' else -1 @@ -278,10 +278,10 @@ CHARTS = { 'innodb_rows': { 'options': [None, 'mysql InnoDB Row Operations', 'operations/s', 'innodb', 'mysql.innodb_rows', 'area'], 'lines': [ - ['Innodb_rows_inserted', 'read', 'incremental'], - ['Innodb_rows_read', 'deleted', 'incremental', -1, 1], - ['Innodb_rows_updated', 'inserted', 'incremental', 1, 1], - ['Innodb_rows_deleted', 'updated', 'incremental', -1, 1], + ['Innodb_rows_inserted', 'inserted', 'incremental'], + ['Innodb_rows_read', 'read', 'incremental', 1, 1], + ['Innodb_rows_updated', 'updated', 'incremental', 1, 1], + ['Innodb_rows_deleted', 'deleted', 'incremental', -1, 1], ]}, 'innodb_buffer_pool_pages': { 'options': [None, 'mysql InnoDB Buffer Pool Pages', 'pages', 'innodb', 'mysql.innodb_buffer_pool_pages', 'line'], diff --git a/python.d/ovpn_status_log.chart.py b/python.d/ovpn_status_log.chart.py index c5fca002a..3a7e8200d 100644 --- a/python.d/ovpn_status_log.chart.py +++ b/python.d/ovpn_status_log.chart.py @@ -2,8 +2,10 @@ # Description: openvpn status log netdata python.d module # Author: l2isbad +from re import compile as r_compile +from collections import defaultdict from base import SimpleService -from re import compile, findall, search, subn + priority = 60000 retries = 60 update_every = 10 @@ -11,67 +13,101 @@ update_every = 10 ORDER = ['users', 'traffic'] CHARTS = { 'users': { - 'options': [None, 'OpenVPN active users', 'active users', 'Users', 'openvpn_status.users', 'line'], + 'options': [None, 'OpenVPN Active Users', 'active users', 'users', 'openvpn_status.users', 'line'], 'lines': [ - ["users", None, "absolute"], + ['users', None, 'absolute'], ]}, 'traffic': { - 'options': [None, 'OpenVPN traffic', 'kilobit/s', 'Traffic', 'openvpn_status.traffic', 'area'], + 'options': [None, 'OpenVPN Traffic', 'KB/s', 'traffic', 'openvpn_status.traffic', 'area'], 'lines': [ - ["in", None, "incremental", 8, 1000], ["out", None, "incremental", 8, -1000] + ['bytes_in', 'in', 'incremental', 1, 1 << 10], ['bytes_out', 'out', 'incremental', 1, -1 << 10] ]}, } + class Service(SimpleService): def __init__(self, configuration=None, name=None): SimpleService.__init__(self, configuration=configuration, name=name) self.order = ORDER self.definitions = CHARTS self.log_path = self.configuration.get('log_path') - self.regex_data_inter = compile(r'(?<=Since ).*?(?=.ROUTING)') - self.regex_data_final = compile(r'\d{1,3}(?:\.\d{1,3}){3}[:0-9,. ]*') - self.regex_users = compile(r'\d{1,3}(?:\.\d{1,3}){3}:\d+') - self.regex_traffic = compile(r'(?<=(?:,| ))\d+(?=(?:,| ))') + self.regex = dict(tls=r_compile(r'\d{1,3}(?:\.\d{1,3}){3}(?::\d+)? (?P\d+) (?P\d+)'), + static_key=r_compile(r'TCP/[A-Z]+ (?P(?:read|write)) bytes,(?P\d+)')) + self.to_netdata = dict(bytes_in=0, bytes_out=0) def check(self): - if not self._get_raw_data(): - self.error('Make sure that the openvpn status log file exists and netdata has permission to read it') + if not (self.log_path and isinstance(self.log_path, str)): + self.error('\'log_path\' is not defined') return False - else: - self.info('Plugin was started succesfully') + + data = False + for method in (self._get_data_tls, self._get_data_static_key): + data = method() + if data: + self._get_data = method + self._data_from_check = data + break + + if data: return True + self.error('Make sure that the openvpn status log file exists and netdata has permission to read it') + return False def _get_raw_data(self): """ Open log file :return: str """ + try: - with open(self.log_path, 'rt') as log: - result = log.read() - except Exception: + with open(self.log_path) as log: + raw_data = log.readlines() or None + except OSError: return None else: - return result + return raw_data - def _get_data(self): + def _get_data_static_key(self): """ Parse openvpn-status log file. - Current regex version is ok for status-version 1, 2 and 3. Both users and bytes in/out are collecting. """ raw_data = self._get_raw_data() - try: - data_inter = self.regex_data_inter.search(' '.join(raw_data.splitlines())).group() - except AttributeError: - data_inter = '' + if not raw_data: + return None + + data = defaultdict(lambda: 0) + + for row in raw_data: + match = self.regex['static_key'].search(row) + if match: + match = match.groupdict() + if match['direction'] == 'read': + data['bytes_in'] += int(match['bytes']) + else: + data['bytes_out'] += int(match['bytes']) + + return data or None + + def _get_data_tls(self): + """ + Parse openvpn-status log file. + """ + + raw_data = self._get_raw_data() + if not raw_data: + return None - data_final = ' '.join(self.regex_data_final.findall(data_inter)) - users = self.regex_users.subn('', data_final)[1] - traffic = self.regex_traffic.findall(data_final) + data = defaultdict(lambda: 0) + for row in raw_data: + row = ' '.join(row.split(',')) if ',' in row else ' '.join(row.split()) + match = self.regex['tls'].search(row) + if match: + match = match.groupdict() + data['users'] += 1 + data['bytes_in'] += int(match['bytes_in']) + data['bytes_out'] += int(match['bytes_out']) - bytes_in = sum([int(traffic[i]) for i in range(len(traffic)) if (i + 1) % 2 is 1]) - bytes_out = sum([int(traffic[i]) for i in range(len(traffic)) if (i + 1) % 2 is 0]) + return data or None - return {'users': users, 'in': bytes_in, 'out': bytes_out} diff --git a/python.d/postgres.chart.py b/python.d/postgres.chart.py index d359bb4f7..ef710cb84 100644 --- a/python.d/postgres.chart.py +++ b/python.d/postgres.chart.py @@ -99,8 +99,8 @@ SELECT sum(conflicts) AS conflicts, pg_database_size(datname) AS size FROM pg_stat_database -WHERE NOT datname ~* '^template\d+' -GROUP BY database_name; +WHERE datname IN %(databases)s +GROUP BY datname; """, BGWRITER=""" SELECT @@ -146,7 +146,6 @@ SELECT current_setting('is_superuser') = 'on' AS is_superuser; QUERY_STATS = { QUERIES['DATABASE']: METRICS['DATABASE'], QUERIES['BACKENDS']: METRICS['BACKENDS'], - QUERIES['ARCHIVE']: METRICS['ARCHIVE'], QUERIES['LOCKS']: METRICS['LOCKS'] } @@ -242,6 +241,7 @@ class Service(SimpleService): self.definitions = deepcopy(CHARTS) self.table_stats = configuration.pop('table_stats', False) self.index_stats = configuration.pop('index_stats', False) + self.database_poll = configuration.pop('database_poll', None) self.configuration = configuration self.connection = False self.is_superuser = False @@ -281,6 +281,9 @@ class Service(SimpleService): is_superuser = check_if_superuser_(cursor, QUERIES['IF_SUPERUSER']) cursor.close() + if (self.database_poll and isinstance(self.database_poll, str)): + self.databases = [dbase for dbase in self.databases if dbase in self.database_poll.split()] or self.databases + self.locks_zeroed = populate_lock_types(self.databases) self.add_additional_queries_(is_superuser) self.create_dynamic_charts_() @@ -296,6 +299,7 @@ class Service(SimpleService): QUERY_STATS[QUERIES['TABLE_STATS']] = METRICS['TABLE_STATS'] if is_superuser: QUERY_STATS[QUERIES['BGWRITER']] = METRICS['BGWRITER'] + QUERY_STATS[QUERIES['ARCHIVE']] = METRICS['ARCHIVE'] def create_dynamic_charts_(self): @@ -328,7 +332,7 @@ class Service(SimpleService): return None def query_stats_(self, cursor, query, metrics): - cursor.execute(query) + cursor.execute(query, dict(databases=tuple(self.databases))) for row in cursor: for metric in metrics: dimension_id = '_'.join([row['database_name'], metric]) if 'database_name' in row else metric diff --git a/python.d/python_modules/base.py b/python.d/python_modules/base.py index 859300eca..a643cc6a0 100644 --- a/python.d/python_modules/base.py +++ b/python.d/python_modules/base.py @@ -20,23 +20,20 @@ import time import os import socket -import select import threading -import msg import ssl from subprocess import Popen, PIPE from sys import exc_info - +from glob import glob +import re try: from urlparse import urlparse except ImportError: from urllib.parse import urlparse - try: import urllib.request as urllib2 except ImportError: import urllib2 - try: import MySQLdb PYMYSQL = True @@ -46,6 +43,7 @@ except ImportError: PYMYSQL = True except ImportError: PYMYSQL = False +import msg try: PATH = os.getenv('PATH').split(':') @@ -175,14 +173,15 @@ class SimpleService(threading.Thread): # it is important to do this in a loop # sleep() is interruptable while now < next: - self.debug("sleeping for", str(next - now), "secs to reach frequency of", str(step), "secs, now:", str(now), " next:", str(next), " penalty:", str(penalty)) + self.debug("sleeping for", str(next - now), "secs to reach frequency of", + str(step), "secs, now:", str(now), " next:", str(next), " penalty:", str(penalty)) time.sleep(next - now) now = float(time.time()) # do the job try: status = self._run_once() - except Exception as e: + except Exception: status = False if status: @@ -202,10 +201,12 @@ class SimpleService(threading.Thread): penalty = 600 self.retries_left = self.retries - self.alert("failed to collect data for " + str(self.retries) + " times - increasing penalty to " + str(penalty) + " sec and trying again") + self.alert("failed to collect data for " + str(self.retries) + + " times - increasing penalty to " + str(penalty) + " sec and trying again") else: - self.error("failed to collect data - " + str(self.retries_left) + " retries left - penalty: " + str(penalty) + " sec") + self.error("failed to collect data - " + str(self.retries_left) + + " retries left - penalty: " + str(penalty) + " sec") # --- CHART --- @@ -460,11 +461,42 @@ class SimpleService(threading.Thread): return next(('/'.join([p, binary]) for p in PATH if os.path.isfile('/'.join([p, binary])) and os.access('/'.join([p, binary]), os.X_OK))) - else: - return None + return None except StopIteration: return None + def _add_new_dimension(self, dimension_id, chart_name, dimension=None, algorithm='incremental', + multiplier=1, divisor=1, priority=65000): + """ + :param dimension_id: + :param chart_name: + :param dimension: + :param algorithm: + :param multiplier: + :param divisor: + :param priority: + :return: + """ + if not all([dimension_id not in self._dimensions, + chart_name in self.order, + chart_name in self.definitions]): + return + self._dimensions.append(dimension_id) + dimension_list = list(map(str, [dimension_id, + dimension if dimension else dimension_id, + algorithm, + multiplier, + divisor])) + self.definitions[chart_name]['lines'].append(dimension_list) + add_to_name = self.override_name or self.name + job_name = ('_'.join([self.__module__, re.sub('\s+', '_', add_to_name)]) + if add_to_name != 'None' else self.__module__) + chart = 'CHART {0}.{1} '.format(job_name, chart_name) + options = '"" "{0}" {1} "{2}" {3} {4} '.format(*self.definitions[chart_name]['options'][1:6]) + other = '{0} {1}\n'.format(priority, self.update_every) + new_dimension = "DIMENSION {0}\n".format(' '.join(dimension_list)) + print(chart + options + other + new_dimension) + class UrlService(SimpleService): def __init__(self, configuration=None, name=None): @@ -473,47 +505,73 @@ class UrlService(SimpleService): self.user = self.configuration.get('user') self.password = self.configuration.get('pass') self.ss_cert = self.configuration.get('ss_cert') + self.proxy = self.configuration.get('proxy') - def __add_openers(self): - def self_signed_cert(ss_cert): - if ss_cert: - try: - ctx = ssl.create_default_context() - ctx.check_hostname = False - ctx.verify_mode = ssl.CERT_NONE - return urllib2.build_opener(urllib2.HTTPSHandler(context=ctx)) - except AttributeError: - return None - else: - return None + def __add_openers(self, user=None, password=None, ss_cert=None, proxy=None, url=None): + user = user or self.user + password = password or self.password + ss_cert = ss_cert or self.ss_cert + proxy = proxy or self.proxy - self.opener = self_signed_cert(self.ss_cert) or urllib2.build_opener() + handlers = list() - # HTTP Basic Auth - if self.user and self.password: - url_parse = urlparse(self.url) + # HTTP Basic Auth handler + if all([user, password, isinstance(user, str), isinstance(password, str)]): + url = url or self.url + url_parse = urlparse(url) top_level_url = '://'.join([url_parse.scheme, url_parse.netloc]) passman = urllib2.HTTPPasswordMgrWithDefaultRealm() - passman.add_password(None, top_level_url, self.user, self.password) - self.opener.add_handler(urllib2.HTTPBasicAuthHandler(passman)) + passman.add_password(None, top_level_url, user, password) + handlers.append(urllib2.HTTPBasicAuthHandler(passman)) self.debug("Enabling HTTP basic auth") - def _get_raw_data(self, custom_url=None): + # HTTPS handler + # Self-signed certificate ignore + if ss_cert: + try: + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + except AttributeError: + self.error('HTTPS self-signed certificate ignore not enabled') + else: + handlers.append(urllib2.HTTPSHandler(context=ctx)) + self.debug("Enabling HTTP self-signed certificate ignore") + + # PROXY handler + if proxy and isinstance(proxy, str) and not ss_cert: + handlers.append(urllib2.ProxyHandler(dict(http=proxy))) + self.debug("Enabling HTTP proxy handler (%s)" % proxy) + + opener = urllib2.build_opener(*handlers) + return opener + + def _build_opener(self, **kwargs): + try: + return self.__add_openers(**kwargs) + except TypeError as error: + self.error('build_opener() error:', str(error)) + return None + + def _get_raw_data(self, url=None, opener=None): """ Get raw data from http request :return: str """ - raw_data = None - f = None + data = None try: - f = self.opener.open(custom_url or self.url, timeout=self.update_every * 2) - raw_data = f.read().decode('utf-8', 'ignore') + opener = opener or self.opener + data = opener.open(url or self.url, timeout=self.update_every * 2) + raw_data = data.read().decode('utf-8', 'ignore') + except urllib2.URLError as error: + self.error('Url: %s. Error: %s' % (url or self.url, str(error))) + return None except Exception as error: - self.error('Url: %s. Error: %s' %(custom_url or self.url, str(error))) + self.error(str(error)) return None finally: - if f is not None: f.close() - + if data is not None: + data.close() return raw_data or None def check(self): @@ -525,7 +583,7 @@ class UrlService(SimpleService): self.error('URL is not defined or type is not ') return False - self.__add_openers() + self.opener = self.__add_openers() try: data = self._get_data() @@ -781,57 +839,69 @@ class SocketService(SimpleService): class LogService(SimpleService): def __init__(self, configuration=None, name=None): - self.log_path = "" - self._last_position = 0 - # self._log_reader = None SimpleService.__init__(self, configuration=configuration, name=name) + self.log_path = self.configuration.get('path') + self.__glob_path = self.log_path + self._last_position = 0 self.retries = 100000 # basically always retry + self.__re_find = dict(current=0, run=0, maximum=60) def _get_raw_data(self): """ Get log lines since last poll :return: list """ - lines = [] + lines = list() try: - if os.path.getsize(self.log_path) < self._last_position: + if self.__re_find['current'] == self.__re_find['run']: + self._find_recent_log_file() + size = os.path.getsize(self.log_path) + if size == self._last_position: + self.__re_find['current'] += 1 + return list() # return empty list if nothing has changed + elif size < self._last_position: self._last_position = 0 # read from beginning if file has shrunk - elif os.path.getsize(self.log_path) == self._last_position: - self.debug("Log file hasn't changed. No new data.") - return [] # return empty list if nothing has changed - with open(self.log_path, "r") as fp: + + with open(self.log_path) as fp: fp.seek(self._last_position) - for i, line in enumerate(fp): + for line in fp: lines.append(line) self._last_position = fp.tell() - except Exception as e: - self.error(str(e)) + self.__re_find['current'] = 0 + except (OSError, IOError) as error: + self.__re_find['current'] += 1 + self.error(str(error)) - if len(lines) != 0: - return lines - else: - self.error("No data collected.") - return None + return lines or None + + def _find_recent_log_file(self): + """ + :return: + """ + self.__re_find['run'] = self.__re_find['maximum'] + self.__re_find['current'] = 0 + self.__glob_path = self.__glob_path or self.log_path # workaround for modules w/o config files + path_list = glob(self.__glob_path) + if path_list: + self.log_path = max(path_list) + return True + return False def check(self): """ Parse basic configuration and check if log file exists :return: boolean """ - if self.name is not None or self.name != str(None): - self.name = "" - else: - self.name = str(self.name) - try: - self.log_path = str(self.configuration['path']) - except (KeyError, TypeError): - self.info("No path to log specified. Using: '" + self.log_path + "'") + if not self.log_path: + self.error("No path to log specified") + return None - if os.access(self.log_path, os.R_OK): + if all([self._find_recent_log_file(), + os.access(self.log_path, os.R_OK), + os.path.isfile(self.log_path)]): return True - else: - self.error("Cannot access file: '" + self.log_path + "'") - return False + self.error("Cannot access %s" % self.log_path) + return False def create(self): # set cursor at last byte of log file @@ -847,7 +917,7 @@ class ExecutableService(SimpleService): SimpleService.__init__(self, configuration=configuration, name=name) self.command = None - def _get_raw_data(self): + def _get_raw_data(self, stderr=False): """ Get raw data from executed command :return: @@ -855,10 +925,11 @@ class ExecutableService(SimpleService): try: p = Popen(self.command, stdout=PIPE, stderr=PIPE) except Exception as error: - self.error("Executing command", self.command, "resulted in error:", str(error)) + self.error("Executing command", " ".join(self.command), "resulted in error:", str(error)) return None data = list() - for line in p.stdout.readlines(): + std = p.stderr if stderr else p.stdout + for line in std.readlines(): data.append(line.decode()) return data or None diff --git a/python.d/rabbitmq.chart.py b/python.d/rabbitmq.chart.py new file mode 100644 index 000000000..15a6a80f7 --- /dev/null +++ b/python.d/rabbitmq.chart.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +# Description: rabbitmq netdata python.d module +# Author: l2isbad + +from base import UrlService +from socket import gethostbyname, gaierror +try: + from queue import Queue +except ImportError: + from Queue import Queue +from threading import Thread +from collections import namedtuple +from json import loads + +# default module values (can be overridden per job in `config`) +update_every = 1 +priority = 60000 +retries = 60 + +METHODS = namedtuple('METHODS', ['get_data_function', 'url', 'stats']) + +NODE_STATS = [('fd_used', None), + ('mem_used', None), + ('sockets_used', None), + ('proc_used', None), + ('disk_free', None) + ] +OVERVIEW_STATS = [('object_totals.channels', None), + ('object_totals.consumers', None), + ('object_totals.connections', None), + ('object_totals.queues', None), + ('object_totals.exchanges', None), + ('queue_totals.messages_ready', None), + ('queue_totals.messages_unacknowledged', None), + ('message_stats.ack', None), + ('message_stats.redeliver', None), + ('message_stats.deliver', None), + ('message_stats.publish', None) + ] +ORDER = ['queued_messages', 'message_rates', 'global_counts', + 'file_descriptors', 'socket_descriptors', 'erlang_processes', 'memory', 'disk_space'] + +CHARTS = { + 'file_descriptors': { + 'options': [None, 'File Descriptors', 'descriptors', 'overview', + 'rabbitmq.file_descriptors', 'line'], + 'lines': [ + ['fd_used', 'used', 'absolute'] + ]}, + 'memory': { + 'options': [None, 'Memory', 'MB', 'overview', + 'rabbitmq.memory', 'line'], + 'lines': [ + ['mem_used', 'used', 'absolute', 1, 1024 << 10] + ]}, + 'disk_space': { + 'options': [None, 'Disk Space', 'GB', 'overview', + 'rabbitmq.disk_space', 'line'], + 'lines': [ + ['disk_free', 'free', 'absolute', 1, 1024 ** 3] + ]}, + 'socket_descriptors': { + 'options': [None, 'Socket Descriptors', 'descriptors', 'overview', + 'rabbitmq.sockets', 'line'], + 'lines': [ + ['sockets_used', 'used', 'absolute'] + ]}, + 'erlang_processes': { + 'options': [None, 'Erlang Processes', 'processes', 'overview', + 'rabbitmq.processes', 'line'], + 'lines': [ + ['proc_used', 'used', 'absolute'] + ]}, + 'global_counts': { + 'options': [None, 'Global Counts', 'counts', 'overview', + 'rabbitmq.global_counts', 'line'], + 'lines': [ + ['channels', None, 'absolute'], + ['consumers', None, 'absolute'], + ['connections', None, 'absolute'], + ['queues', None, 'absolute'], + ['exchanges', None, 'absolute'] + ]}, + 'queued_messages': { + 'options': [None, 'Queued Messages', 'messages', 'overview', + 'rabbitmq.queued_messages', 'stacked'], + 'lines': [ + ['messages_ready', 'ready', 'absolute'], + ['messages_unacknowledged', 'unacknowledged', 'absolute'] + ]}, + 'message_rates': { + 'options': [None, 'Message Rates', 'messages/s', 'overview', + 'rabbitmq.message_rates', 'stacked'], + 'lines': [ + ['ack', None, 'incremental'], + ['redeliver', None, 'incremental'], + ['deliver', None, 'incremental'], + ['publish', None, 'incremental'] + ]} +} + + +class Service(UrlService): + def __init__(self, configuration=None, name=None): + UrlService.__init__(self, configuration=configuration, name=name) + self.order = ORDER + self.definitions = CHARTS + self.host = self.configuration.get('host', '127.0.0.1') + self.port = self.configuration.get('port', 15672) + self.scheme = self.configuration.get('scheme', 'http') + + def check(self): + # We can't start if AND not specified + if not (self.host and self.port): + self.error('Host is not defined in the module configuration file') + return False + + # Hostname -> ip address + try: + self.host = gethostbyname(self.host) + except gaierror as error: + self.error(str(error)) + return False + + # Add handlers (auth, self signed cert accept) + url = '%s://%s:%s/api' % (self.scheme, self.host, self.port) + self.opener = self._build_opener(url=url) + if not self.opener: + return False + # Add methods + api_node = url + '/nodes' + api_overview = url + '/overview' + self.methods = [METHODS(get_data_function=self._get_overview_stats, url=api_node, stats=NODE_STATS), + METHODS(get_data_function=self._get_overview_stats, url=api_overview, stats=OVERVIEW_STATS)] + + result = self._get_data() + if not result: + self.error('_get_data() returned no data') + return False + self._data_from_check = result + return True + + def _get_data(self): + threads = list() + queue = Queue() + result = dict() + + for method in self.methods: + th = Thread(target=method.get_data_function, args=(queue, method.url, method.stats)) + th.start() + threads.append(th) + + for thread in threads: + thread.join() + result.update(queue.get()) + + return result or None + + def _get_overview_stats(self, queue, url, stats): + """ + Format data received from http request + :return: dict + """ + + raw_data = self._get_raw_data(url) + + if not raw_data: + return queue.put(dict()) + data = loads(raw_data) + data = data[0] if isinstance(data, list) else data + + to_netdata = fetch_data_(raw_data=data, metrics_list=stats) + return queue.put(to_netdata) + + +def fetch_data_(raw_data, metrics_list): + to_netdata = dict() + for metric, new_name in metrics_list: + value = raw_data + for key in metric.split('.'): + try: + value = value[key] + except (KeyError, TypeError): + break + if not isinstance(value, dict): + to_netdata[new_name or key] = value + return to_netdata diff --git a/python.d/redis.chart.py b/python.d/redis.chart.py index 61f4f6d61..4bc1d41f9 100644 --- a/python.d/redis.chart.py +++ b/python.d/redis.chart.py @@ -100,13 +100,12 @@ class Service(SocketService): :return: dict """ if self.passwd: - info_request = self.request self.request = "AUTH " + self.passwd + "\r\n" raw = self._get_raw_data().strip() if raw != "+OK": self.error("invalid password") return None - self.request = info_request + self.request = "INFO\r\n" response = self._get_raw_data() if response is None: # error has already been logged @@ -155,7 +154,7 @@ class Service(SocketService): :return: boolean """ length = len(data) - supposed = data.split('\n')[0][1:] + supposed = data.split('\n')[0][1:-1] offset = len(supposed) + 4 # 1 dollar sing, 1 new line character + 1 ending sequence '\r\n' if not supposed.isdigit(): return True diff --git a/python.d/samba.chart.py b/python.d/samba.chart.py new file mode 100644 index 000000000..767c97469 --- /dev/null +++ b/python.d/samba.chart.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +# Description: samba netdata python.d module +# Author: Christopher Cox +# +# The netdata user needs to be able to be able to sudo the smbstatus program +# without password: +# netdata ALL=(ALL) NOPASSWD: /usr/bin/smbstatus -P +# +# This makes calls to smbstatus -P +# +# This just looks at a couple of values out of syscall, and some from smb2. +# +# The Lesser Ops chart is merely a display of current counter values. They +# didn't seem to change much to me. However, if you notice something changing +# a lot there, bring one or more out into its own chart and make it incremental +# (like find and notify... good examples). + +from base import ExecutableService +import re + +# default module values (can be overridden per job in `config`) +update_every = 5 +priority = 60000 +retries = 60 + +ORDER = ['syscall_rw','smb2_rw','smb2_create_close','smb2_info','smb2_find','smb2_notify','smb2_sm_count'] + +CHARTS = { + 'syscall_rw': { + 'lines': [ + ['syscall_sendfile_bytes', 'sendfile', 'incremental', 1, 1024], + ['syscall_recvfile_bytes', 'recvfile', 'incremental', -1, 1024] + ], + 'options': [None, 'R/Ws', 'kilobytes/s', 'syscall', 'syscall.rw', 'area'] + }, + 'smb2_rw': { + 'lines': [ + ['smb2_read_outbytes', 'readout', 'incremental', 1, 1024], + ['smb2_write_inbytes', 'writein', 'incremental', -1, 1024], + ['smb2_read_inbytes', 'readin', 'incremental', 1, 1024], + ['smb2_write_outbytes', 'writeout', 'incremental', -1, 1024] + ], + 'options': [None, 'R/Ws', 'kilobytes/s', 'smb2', 'smb2.rw', 'area'] + }, + 'smb2_create_close': { + 'lines': [ + ['smb2_create_count', 'create', 'incremental', 1, 1], + ['smb2_close_count', 'close', 'incremental', -1, 1] + ], + 'options': [None, 'Create/Close', 'operations/s', 'smb2', 'smb2.create_close', 'line'] + }, + 'smb2_info': { + 'lines': [ + ['smb2_getinfo_count', 'getinfo', 'incremental', 1, 1], + ['smb2_setinfo_count', 'setinfo', 'incremental', -1, 1] + ], + 'options': [None, 'Info', 'operations/s', 'smb2', 'smb2.get_set_info', 'line'] + }, + 'smb2_find': { + 'lines': [ + ['smb2_find_count', 'find', 'incremental', 1, 1] + ], + 'options': [None, 'Find', 'operations/s', 'smb2', 'smb2.find', 'line'] + }, + 'smb2_notify': { + 'lines': [ + ['smb2_notify_count', 'notify', 'incremental', 1, 1] + ], + 'options': [None, 'Notify', 'operations/s', 'smb2', 'smb2.notify', 'line'] + }, + 'smb2_sm_count': { + 'lines': [ + ['smb2_tcon_count', 'tcon', 'absolute', 1, 1], + ['smb2_negprot_count', 'negprot', 'absolute', 1, 1], + ['smb2_tdis_count', 'tdis', 'absolute', 1, 1], + ['smb2_cancel_count', 'cancel', 'absolute', 1, 1], + ['smb2_logoff_count', 'logoff', 'absolute', 1, 1], + ['smb2_flush_count', 'flush', 'absolute', 1, 1], + ['smb2_lock_count', 'lock', 'absolute', 1, 1], + ['smb2_keepalive_count', 'keepalive', 'absolute', 1, 1], + ['smb2_break_count', 'break', 'absolute', 1, 1], + ['smb2_sessetup_count', 'sessetup', 'absolute', 1, 1] + ], + 'options': [None, 'Lesser Ops', 'count', 'smb2', 'smb2.sm_counters', 'stacked'] + } + } + + +class Service(ExecutableService): + def __init__(self, configuration=None, name=None): + ExecutableService.__init__(self, configuration=configuration, name=name) + self.order = ORDER + self.definitions = CHARTS + self.rgx_smb2 = re.compile(r'(smb2_[^:]+|syscall_.*file_bytes):\s+(\d+)') + + def check(self): + sudo_binary, smbstatus_binary = self.find_binary('sudo'), self.find_binary('smbstatus') + + if not (sudo_binary and smbstatus_binary): + self.error('Can\'t locate \'sudo\' or \'smbstatus\' binary') + return False + + self.command = [sudo_binary, '-v'] + err = self._get_raw_data(stderr=True) + if err: + self.error(''.join(err)) + return False + + self.command = ' '.join([sudo_binary, '-n', smbstatus_binary, '-P']) + + return ExecutableService.check(self) + + def _get_data(self): + """ + Format data received from shell command + :return: dict + """ + raw_data = self._get_raw_data() + if not raw_data: + return None + + parsed = self.rgx_smb2.findall(' '.join(raw_data)) + + return dict(parsed) or None diff --git a/python.d/smartd_log.chart.py b/python.d/smartd_log.chart.py index e80372379..4039c1536 100644 --- a/python.d/smartd_log.chart.py +++ b/python.d/smartd_log.chart.py @@ -2,7 +2,7 @@ # Description: smart netdata python.d module # Author: l2isbad, vorph1 -from re import compile +from re import compile as r_compile from os import listdir, access, R_OK from os.path import isfile, join, getsize, basename, isdir try: @@ -101,7 +101,7 @@ NAMED_DISKS = namedtuple('disks', ['name', 'size', 'number']) class Service(SimpleService): def __init__(self, configuration=None, name=None): SimpleService.__init__(self, configuration=configuration, name=name) - self.regex = compile(r'(\d+);(\d+);(\d+)') + self.regex = r_compile(r'(\d+);(\d+);(\d+)') self.log_path = self.configuration.get('log_path', '/var/log/smartd') self.raw_values = self.configuration.get('raw_values') self.attr = self.configuration.get('smart_attributes', []) @@ -197,18 +197,19 @@ class Service(SimpleService): result.append(['_'.join([name, attrid]), name[:name.index('.')], 'absolute']) return result - # Add additional smart attributes to the ORDER. If something goes wrong we don't care. + # Use configured attributes, if present. If something goes wrong we don't care. + order = ORDER try: - ORDER.extend(list(set(self.attr.split()) & SMART_ATTR.keys() - set(ORDER))) + order = [attr for attr in self.attr.split() if attr in SMART_ATTR.keys()] or ORDER except Exception: pass - self.order = [''.join(['attrid', i]) for i in ORDER] + self.order = [''.join(['attrid', i]) for i in order] self.definitions = dict() units = 'raw' if self.raw_values else 'normalized' for k, v in dict([(k, v) for k, v in SMART_ATTR.items() if k in ORDER]).items(): self.definitions.update({''.join(['attrid', k]): { - 'options': [None, v, units, v, 'smartd.attrid' + k, 'line'], + 'options': [None, v, units, v.lower(), 'smartd.attrid' + k, 'line'], 'lines': create_lines(k)}}) def find_disks_in_log_path(log_path): diff --git a/python.d/web_log.chart.py b/python.d/web_log.chart.py index cbc8cd235..564c9f1dd 100644 --- a/python.d/web_log.chart.py +++ b/python.d/web_log.chart.py @@ -1,37 +1,51 @@ # -*- coding: utf-8 -*- # Description: web log netdata python.d module # Author: l2isbad - -from base import LogService import re import bisect from os import access, R_OK from os.path import getsize -from collections import namedtuple +from collections import namedtuple, defaultdict from copy import deepcopy +try: + from itertools import filterfalse +except ImportError: + from itertools import ifilterfalse as filterfalse +from base import LogService +import msg + priority = 60000 retries = 60 -ORDER = ['response_statuses', 'response_codes', 'bandwidth', 'response_time', 'requests_per_url', 'http_method', - 'http_version', 'requests_per_ipproto', 'clients', 'clients_all'] -CHARTS = { +ORDER_APACHE_CACHE = ['apache_cache'] + +ORDER_WEB = ['response_statuses', 'response_codes', 'bandwidth', 'response_time', 'response_time_upstream', + 'requests_per_url', 'requests_per_user_defined', 'http_method', 'http_version', + 'requests_per_ipproto', 'clients', 'clients_all'] + +ORDER_SQUID = ['squid_response_statuses', 'squid_response_codes', 'squid_detailed_response_codes', + 'squid_method', 'squid_mime_type', 'squid_hier_code', 'squid_transport_methods', + 'squid_transport_errors', 'squid_code', 'squid_handling_opts', 'squid_object_types', + 'squid_cache_events', 'squid_bytes', 'squid_duration', 'squid_clients', 'squid_clients_all'] + +CHARTS_WEB = { 'response_codes': { 'options': [None, 'Response Codes', 'requests/s', 'responses', 'web_log.response_codes', 'stacked'], 'lines': [ - ['2xx', '2xx', 'incremental'], - ['5xx', '5xx', 'incremental'], - ['3xx', '3xx', 'incremental'], - ['4xx', '4xx', 'incremental'], - ['1xx', '1xx', 'incremental'], + ['2xx', None, 'incremental'], + ['5xx', None, 'incremental'], + ['3xx', None, 'incremental'], + ['4xx', None, 'incremental'], + ['1xx', None, 'incremental'], ['0xx', 'other', 'incremental'], - ['unmatched', 'unmatched', 'incremental'] + ['unmatched', None, 'incremental'] ]}, 'bandwidth': { - 'options': [None, 'Bandwidth', 'KB/s', 'bandwidth', 'web_log.bandwidth', 'area'], + 'options': [None, 'Bandwidth', 'kilobits/s', 'bandwidth', 'web_log.bandwidth', 'area'], 'lines': [ - ['resp_length', 'received', 'incremental', 1, 1024], - ['bytes_sent', 'sent', 'incremental', -1, 1024] + ['resp_length', 'received', 'incremental', 8, 1000], + ['bytes_sent', 'sent', 'incremental', -8, 1000] ]}, 'response_time': { 'options': [None, 'Processing Time', 'milliseconds', 'timings', 'web_log.response_time', 'area'], @@ -40,6 +54,14 @@ CHARTS = { ['resp_time_max', 'max', 'incremental', 1, 1000], ['resp_time_avg', 'avg', 'incremental', 1, 1000] ]}, + 'response_time_upstream': { + 'options': [None, 'Processing Time Upstream', 'milliseconds', 'timings', + 'web_log.response_time_upstream', 'area'], + 'lines': [ + ['resp_time_upstream_min', 'min', 'incremental', 1, 1000], + ['resp_time_upstream_max', 'max', 'incremental', 1, 1000], + ['resp_time_upstream_avg', 'avg', 'incremental', 1, 1000] + ]}, 'clients': { 'options': [None, 'Current Poll Unique Client IPs', 'unique ips', 'clients', 'web_log.clients', 'stacked'], 'lines': [ @@ -77,36 +99,160 @@ CHARTS = { ['redirects', 'redirect', 'incremental', 1, 1], ['bad_requests', 'bad', 'incremental', 1, 1], ['other_requests', 'other', 'incremental', 1, 1] + ]}, + 'requests_per_url': { + 'options': [None, 'Requests Per Url', 'requests/s', 'urls', 'web_log.requests_per_url', + 'stacked'], + 'lines': [ + ['url_pattern_other', 'other', 'incremental', 1, 1] + ]}, + 'requests_per_user_defined': { + 'options': [None, 'Requests Per User Defined Pattern', 'requests/s', 'user defined', + 'web_log.requests_per_user_defined', 'stacked'], + 'lines': [ + ['user_pattern_other', 'other', 'incremental', 1, 1] ]} } -NAMED_URL_PATTERN = namedtuple('URL_PATTERN', ['description', 'pattern']) +CHARTS_APACHE_CACHE = { + 'apache_cache': { + 'options': [None, 'Apache Cached Responses', 'percent cached', 'cached', 'web_log.apache_cache_cache', + 'stacked'], + 'lines': [ + ["hit", 'cache', "percentage-of-absolute-row"], + ["miss", None, "percentage-of-absolute-row"], + ["other", None, "percentage-of-absolute-row"] + ]} +} + +CHARTS_SQUID = { + 'squid_duration': { + 'options': [None, 'Elapsed Time The Transaction Busied The Cache', + 'milliseconds', 'squid_timings', 'web_log.squid_duration', 'area'], + 'lines': [ + ['duration_min', 'min', 'incremental', 1, 1000], + ['duration_max', 'max', 'incremental', 1, 1000], + ['duration_avg', 'avg', 'incremental', 1, 1000] + ]}, + 'squid_bytes': { + 'options': [None, 'Amount Of Data Delivered To The Clients', + 'kilobits/s', 'squid_bandwidth', 'web_log.squid_bytes', 'area'], + 'lines': [ + ['bytes', 'sent', 'incremental', 8, 1000] + ]}, + 'squid_response_statuses': { + 'options': [None, 'Response Statuses', 'responses/s', 'squid_responses', 'web_log.squid_response_statuses', + 'stacked'], + 'lines': [ + ['successful_requests', 'success', 'incremental', 1, 1], + ['server_errors', 'error', 'incremental', 1, 1], + ['redirects', 'redirect', 'incremental', 1, 1], + ['bad_requests', 'bad', 'incremental', 1, 1], + ['other_requests', 'other', 'incremental', 1, 1] + ]}, + 'squid_response_codes': { + 'options': [None, 'Response Codes', 'responses/s', 'squid_responses', + 'web_log.squid_response_codes', 'stacked'], + 'lines': [ + ['2xx', None, 'incremental'], + ['5xx', None, 'incremental'], + ['3xx', None, 'incremental'], + ['4xx', None, 'incremental'], + ['1xx', None, 'incremental'], + ['0xx', None, 'incremental'], + ['other', None, 'incremental'], + ['unmatched', None, 'incremental'] + ]}, + 'squid_code': { + 'options': [None, 'Responses Per Cache Result Of The Request', + 'requests/s', 'squid_squid_cache', 'web_log.squid_code', 'stacked'], + 'lines': [ + ]}, + 'squid_detailed_response_codes': { + 'options': [None, 'Detailed Response Codes', + 'responses/s', 'squid_responses', 'web_log.squid_detailed_response_codes', 'stacked'], + 'lines': [ + ]}, + 'squid_hier_code': { + 'options': [None, 'Responses Per Hierarchy Code', + 'requests/s', 'squid_hierarchy', 'web_log.squid_hier_code', 'stacked'], + 'lines': [ + ]}, + 'squid_method': { + 'options': [None, 'Requests Per Method', + 'requests/s', 'squid_requests', 'web_log.squid_method', 'stacked'], + 'lines': [ + ]}, + 'squid_mime_type': { + 'options': [None, 'Requests Per MIME Type', + 'requests/s', 'squid_requests', 'web_log.squid_mime_type', 'stacked'], + 'lines': [ + ]}, + 'squid_clients': { + 'options': [None, 'Current Poll Unique Client IPs', 'unique ips', 'squid_clients', + 'web_log.squid_clients', 'stacked'], + 'lines': [ + ['unique_ipv4', 'ipv4', 'incremental'], + ['unique_ipv6', 'ipv6', 'incremental'] + ]}, + 'squid_clients_all': { + 'options': [None, 'All Time Unique Client IPs', 'unique ips', 'squid_clients', + 'web_log.squid_clients_all', 'stacked'], + 'lines': [ + ['unique_tot_ipv4', 'ipv4', 'absolute'], + ['unique_tot_ipv6', 'ipv6', 'absolute'] + ]}, + 'squid_transport_methods': { + 'options': [None, 'Transport Methods', 'requests/s', 'squid_squid_transport', + 'web_log.squid_transport_methods', 'stacked'], + 'lines': [ + ]}, + 'squid_transport_errors': { + 'options': [None, 'Transport Errors', 'requests/s', 'squid_squid_transport', + 'web_log.squid_transport_errors', 'stacked'], + 'lines': [ + ]}, + 'squid_handling_opts': { + 'options': [None, 'Handling Opts', 'requests/s', 'squid_squid_cache', + 'web_log.squid_handling_opts', 'stacked'], + 'lines': [ + ]}, + 'squid_object_types': { + 'options': [None, 'Object Types', 'objects/s', 'squid_squid_cache', + 'web_log.squid_object_types', 'stacked'], + 'lines': [ + ]}, + 'squid_cache_events': { + 'options': [None, 'Cache Events', 'events/s', 'squid_squid_cache', + 'web_log.squid_cache_events', 'stacked'], + 'lines': [ + ]} +} + +NAMED_PATTERN = namedtuple('PATTERN', ['description', 'func']) DET_RESP_AGGR = ['', '_1xx', '_2xx', '_3xx', '_4xx', '_5xx', '_Other'] +SQUID_CODES = dict(TCP='squid_transport_methods', UDP='squid_transport_methods', NONE='squid_transport_methods', + CLIENT='squid_handling_opts', IMS='squid_handling_opts', ASYNC='squid_handling_opts', + SWAPFAIL='squid_handling_opts', REFRESH='squid_handling_opts', SHARED='squid_handling_opts', + REPLY='squid_handling_opts', NEGATIVE='squid_object_types', STALE='squid_object_types', + OFFLINE='squid_object_types', INVALID='squid_object_types', FAIL='squid_object_types', + MODIFIED='squid_object_types', UNMODIFIED='squid_object_types', REDIRECT='squid_object_types', + HIT='squid_cache_events', MEM='squid_cache_events', MISS='squid_cache_events', + DENIED='squid_cache_events', NOFETCH='squid_cache_events', TUNNEL='squid_cache_events', + ABORTED='squid_transport_errors', TIMEOUT='squid_transport_errors') + class Service(LogService): def __init__(self, configuration=None, name=None): """ :param configuration: :param name: - # self._get_data = None # will be assigned in 'check' method. - # self.order = None # will be assigned in 'create_*_method' method. - # self.definitions = None # will be assigned in 'create_*_method' method. """ LogService.__init__(self, configuration=configuration, name=name) - # Variables from module configuration file - self.log_type = self.configuration.get('type', 'web_access') + self.log_type = self.configuration.get('type', 'web') self.log_path = self.configuration.get('path') - self.url_pattern = self.configuration.get('categories') # dict - self.custom_log_format = self.configuration.get('custom_log_format') # dict - # Instance variables - self.regex = None # will be assigned in 'find_regex' or 'find_regex_custom' method - self.data = {'bytes_sent': 0, 'resp_length': 0, 'resp_time_min': 0, 'resp_time_max': 0, - 'resp_time_avg': 0, 'unique_cur_ipv4': 0, 'unique_cur_ipv6': 0, '2xx': 0, - '5xx': 0, '3xx': 0, '4xx': 0, '1xx': 0, '0xx': 0, 'unmatched': 0, 'req_ipv4': 0, - 'req_ipv6': 0, 'unique_tot_ipv4': 0, 'unique_tot_ipv6': 0, 'successful_requests': 0, - 'redirects': 0, 'bad_requests': 0, 'server_errors': 0, 'other_requests': 0, 'GET': 0} def check(self): """ @@ -117,11 +263,18 @@ class Service(LogService): 3. "log_path' must not be empty. We need at least 1 line to find appropriate pattern to parse 4. other checks depends on log "type" """ + + log_types = dict(web=Web, apache_cache=ApacheCache, squid=Squid) + + if self.log_type not in log_types: + self.error('bad log type (%s). Supported types: %s' % (self.log_type, log_types.keys())) + return False + if not self.log_path: self.error('log path is not specified') return False - if not access(self.log_path, R_OK): + if not (self._find_recent_log_file() and access(self.log_path, R_OK)): self.error('%s not readable or not exist' % self.log_path) return False @@ -129,357 +282,237 @@ class Service(LogService): self.error('%s is empty' % self.log_path) return False + self.configuration['update_every'] = self.update_every + self.configuration['name'] = self.name + self.configuration['override_name'] = self.override_name + self.configuration['_dimensions'] = self._dimensions + self.configuration['path'] = self.log_path + + cls = log_types[self.log_type] + self.Job = cls(configuration=self.configuration) + if self.Job.check(): + self.order = self.Job.order + self.definitions = self.Job.definitions + self.info('Current log file: %s' % self.log_path) + return True + return False + + def _get_data(self): + return self.Job.get_data(self._get_raw_data()) + + +class Mixin: + def filter_data(self, raw_data): + """ + :param raw_data: list + :return: + """ + if not self.pre_filter: + return raw_data + filtered = raw_data + for elem in self.pre_filter: + if elem.description == 'filter_include': + filtered = filter(elem.func, filtered) + elif elem.description == 'filter_exclude': + filtered = filterfalse(elem.func, filtered) + return filtered + + def add_new_dimension(self, dimension_id, chart_key, dimension=None, + algorithm='incremental', multiplier=1, divisor=1): + """ + :param dimension: + :param chart_key: + :param dimension_id: + :param algorithm: + :param multiplier: + :param divisor: + :return: + """ + + self.data[dimension_id] = 0 + # SET method check if dim in _dimensions + self.conf['_dimensions'].append(dimension_id) + # UPDATE method do SET only if dim in definitions + dimension_list = list(map(str, [dimension_id, + dimension if dimension else dimension_id, + algorithm, + multiplier, + divisor])) + self.definitions[chart_key]['lines'].append(dimension_list) + job_name = find_job_name(self.conf['override_name'], self.conf['name']) + opts = self.definitions[chart_key]['options'] + chart = 'CHART %s.%s "" "%s" %s "%s" %s %s 60000 %s\n' % (job_name, chart_key, + opts[1], opts[2], opts[3], + opts[4], opts[5], self.conf['update_every']) + print(chart + "DIMENSION %s\n" % ' '.join(dimension_list)) + + def get_last_line(self): + """ + Reads last line from the log file + :return: str: + """ # Read last line (or first if there is only one line) - with open(self.log_path, 'rb') as logs: + with open(self.conf['path'], 'rb') as logs: logs.seek(-2, 2) while logs.read(1) != b'\n': logs.seek(-2, 1) if logs.tell() == 0: break last_line = logs.readline() - try: - last_line = last_line.decode() + return last_line.decode() except UnicodeDecodeError: try: - last_line = last_line.decode(encoding='utf-8') + return last_line.decode(encoding='utf-8') except (TypeError, UnicodeDecodeError) as error: - self.error(str(error)) + msg.error('web_log', str(error)) return False - if self.log_type == 'web_access': - self.unique_all_time = list() # sorted list of unique IPs - self.detailed_response_codes = self.configuration.get('detailed_response_codes', True) - self.detailed_response_aggregate = self.configuration.get('detailed_response_aggregate', True) - self.all_time = self.configuration.get('all_time', True) + @staticmethod + def error(*params): + msg.error('web_log', ' '.join(map(str, params))) - # Custom_log_format or predefined log format. - if self.custom_log_format: - match_dict, error = self.find_regex_custom(last_line) - else: - match_dict, error = self.find_regex(last_line) + @staticmethod + def info(*params): + msg.info('web_log', ' '.join(map(str, params))) - # "match_dict" is None if there are any problems - if match_dict is None: - self.error(str(error)) - return False - # self.url_pattern check - if self.url_pattern: - self.url_pattern = check_req_per_url_pattern('rpu', self.url_pattern) +class Web(Mixin): + def __init__(self, configuration): + self.conf = configuration + self.pre_filter = check_patterns('filter', self.conf.get('filter')) + self.storage = dict() + self.data = {'bytes_sent': 0, 'resp_length': 0, 'resp_time_min': 0, 'resp_time_max': 0, + 'resp_time_avg': 0, 'resp_time_upstream_min': 0, 'resp_time_upstream_max': 0, + 'resp_time_upstream_avg': 0, 'unique_cur_ipv4': 0, 'unique_cur_ipv6': 0, '2xx': 0, + '5xx': 0, '3xx': 0, '4xx': 0, '1xx': 0, '0xx': 0, 'unmatched': 0, 'req_ipv4': 0, + 'req_ipv6': 0, 'unique_tot_ipv4': 0, 'unique_tot_ipv6': 0, 'successful_requests': 0, + 'redirects': 0, 'bad_requests': 0, 'server_errors': 0, 'other_requests': 0, 'GET': 0} - self.create_access_charts(match_dict) # Create charts - self._get_data = self._get_access_data # _get_data assignment - else: - self.error('Not implemented') + def check(self): + last_line = self.get_last_line() + if not last_line: return False - - # Double check - if not self.regex: - self.error('That can not happen, but it happened. "regex" is None') - - self.info('Collected data: %s' % list(match_dict.keys())) - return True - - def find_regex_custom(self, last_line): - """ - :param last_line: str: literally last line from log file - :return: tuple where: - [0]: dict or None: match_dict or None - [1]: str: error description - - We are here only if "custom_log_format" is in logs. We need to make sure: - 1. "custom_log_format" is a dict - 2. "pattern" in "custom_log_format" and pattern is instance - 3. if "time_multiplier" is in "custom_log_format" it must be instance - - If all parameters is ok we need to make sure: - 1. Pattern search is success - 2. Pattern search contains named subgroups (?P) (= "match_dict") - - If pattern search is success we need to make sure: - 1. All mandatory keys ['address', 'code', 'bytes_sent', 'method', 'url'] are in "match_dict" - - If this is True we need to make sure: - 1. All mandatory key values from "match_dict" have the correct format - ("code" is integer, "method" is uppercase word, etc) - - If non mandatory keys in "match_dict" we need to make sure: - 1. All non mandatory key values from match_dict ['resp_length', 'resp_time'] have the correct format - ("resp_length" is integer or "-", "resp_time" is integer or float) - - """ - if not hasattr(self.custom_log_format, 'keys'): - return find_regex_return(msg='Custom log: "custom_log_format" is not a ') - - pattern = self.custom_log_format.get('pattern') - if not (pattern and isinstance(pattern, str)): - return find_regex_return(msg='Custom log: "pattern" option is not specified or type is not ') - - resp_time_func = self.custom_log_format.get('time_multiplier') or 0 - - if not isinstance(resp_time_func, int): - return find_regex_return(msg='Custom log: "time_multiplier" is not an integer') - - try: - regex = re.compile(pattern) - except re.error as error: - return find_regex_return(msg='Pattern compile error: %s' % str(error)) - - match = regex.search(last_line) - if match: - match_dict = match.groupdict() or None + # Custom_log_format or predefined log format. + if self.conf.get('custom_log_format'): + match_dict, error = self.find_regex_custom(last_line) else: - return find_regex_return(msg='Custom log: pattern search FAILED') + match_dict, error = self.find_regex(last_line) + # "match_dict" is None if there are any problems if match_dict is None: - find_regex_return(msg='Custom log: search OK but contains no named subgroups' - ' (you need to use ?P)') - else: - mandatory_dict = {'address': r'[\da-f.:]+', - 'code': r'[1-9]\d{2}', - 'method': r'[A-Z]+', - 'bytes_sent': r'\d+|-'} - optional_dict = {'resp_length': r'\d+', - 'resp_time': r'[\d.]+', - 'http_version': r'\d\.\d'} - - mandatory_values = set(mandatory_dict) - set(match_dict) - if mandatory_values: - return find_regex_return(msg='Custom log: search OK but some mandatory keys (%s) are missing' - % list(mandatory_values)) - else: - for key in mandatory_dict: - if not re.search(mandatory_dict[key], match_dict[key]): - return find_regex_return(msg='Custom log: can\'t parse "%s": %s' - % (key, match_dict[key])) - - optional_values = set(optional_dict) & set(match_dict) - for key in optional_values: - if not re.search(optional_dict[key], match_dict[key]): - return find_regex_return(msg='Custom log: can\'t parse "%s": %s' - % (key, match_dict[key])) - - dot_in_time = '.' in match_dict.get('resp_time', '') - if dot_in_time: - self.resp_time_func = lambda time: time * (resp_time_func or 1000000) - else: - self.resp_time_func = lambda time: time * (resp_time_func or 1) - - self.regex = regex - return find_regex_return(match_dict=match_dict) - - def find_regex(self, last_line): - """ - :param last_line: str: literally last line from log file - :return: tuple where: - [0]: dict or None: match_dict or None - [1]: str: error description - We need to find appropriate pattern for current log file - All logic is do a regex search through the string for all predefined patterns - until we find something or fail. - """ - # REGEX: 1.IPv4 address 2.HTTP method 3. URL 4. Response code - # 5. Bytes sent 6. Response length 7. Response process time - acs_default = re.compile(r'(?P
    [\da-f.:]+)' - r' -.*?"(?P[A-Z]+)' - r' (?P[^ ]+)' - r' [A-Z]+/(?P\d\.\d)"' - r' (?P[1-9]\d{2})' - r' (?P\d+|-)') - - acs_apache_ext_insert = re.compile(r'(?P
    [\da-f.:]+)' - r' -.*?"(?P[A-Z]+)' - r' (?P[^ ]+)' - r' [A-Z]+/(?P\d\.\d)"' - r' (?P[1-9]\d{2})' - r' (?P\d+|-)' - r' (?P\d+)' - r' (?P\d+) ') - - acs_apache_ext_append = re.compile(r'(?P
    [\da-f.:]+)' - r' -.*?"(?P[A-Z]+)' - r' (?P[^ ]+)' - r' [A-Z]+/(?P\d\.\d)"' - r' (?P[1-9]\d{2})' - r' (?P\d+|-)' - r' .*?' - r' (?P\d+)' - r' (?P\d+)' - r'(?: |$)') - - acs_nginx_ext_insert = re.compile(r'(?P
    [\da-f.:]+)' - r' -.*?"(?P[A-Z]+)' - r' (?P[^ ]+)' - r' [A-Z]+/(?P\d\.\d)"' - r' (?P[1-9]\d{2})' - r' (?P\d+)' - r' (?P\d+)' - r' (?P\d+\.\d+) ') - - acs_nginx_ext_append = re.compile(r'(?P
    [\da-f.:]+)' - r' -.*?"(?P[A-Z]+)' - r' (?P[^ ]+)' - r' [A-Z]+/(?P\d\.\d)"' - r' (?P[1-9]\d{2})' - r' (?P\d+)' - r' .*?' - r' (?P\d+)' - r' (?P\d+\.\d+)') - - def func_usec(time): - return time - - def func_sec(time): - return time * 1000000 - - r_regex = [acs_apache_ext_insert, acs_apache_ext_append, acs_nginx_ext_insert, - acs_nginx_ext_append, acs_default] - r_function = [func_usec, func_usec, func_sec, func_sec, func_usec] - regex_function = zip(r_regex, r_function) - - match_dict = dict() - for regex, function in regex_function: - match = regex.search(last_line) - if match: - self.regex = regex - self.resp_time_func = function - match_dict = match.groupdict() - break + self.error(str(error)) + return False + self.storage['unique_all_time'] = list() + self.storage['url_pattern'] = check_patterns('url_pattern', self.conf.get('categories')) + self.storage['user_pattern'] = check_patterns('user_pattern', self.conf.get('user_defined')) - return find_regex_return(match_dict=match_dict or None, - msg='Unknown log format. You need to use "custom_log_format" feature.') + self.create_web_charts(match_dict) # Create charts + self.info('Collected data: %s' % list(match_dict.keys())) + return True - def create_access_charts(self, match_dict): + def create_web_charts(self, match_dict): """ :param match_dict: dict: regex.search.groupdict(). Ex. {'address': '127.0.0.1', 'code': '200', 'method': 'GET'} :return: - Create additional charts depending on the 'match_dict' keys and configuration file options - 1. 'time_response' chart is removed if there is no 'resp_time' in match_dict. - 2. Other stuff is just remove/add chart depending on yes/no in conf + Create/remove additional charts depending on the 'match_dict' keys and configuration file options """ + self.order = ORDER_WEB[:] + self.definitions = deepcopy(CHARTS_WEB) - def find_job_name(override_name, name): - """ - :param override_name: str: 'name' var from configuration file - :param name: str: 'job_name' from configuration file - :return: str: new job name - We need this for dynamic charts. Actually same logic as in python.d.plugin. - """ - add_to_name = override_name or name - if add_to_name: - return '_'.join(['web_log', re.sub('\s+', '_', add_to_name)]) - else: - return 'web_log' - - self.order = ORDER[:] - self.definitions = deepcopy(CHARTS) - - job_name = find_job_name(self.override_name, self.name) - - self.http_method_chart = 'CHART %s.http_method' \ - ' "" "Requests Per HTTP Method" requests/s "http methods"' \ - ' web_log.http_method stacked 11 %s\n' \ - 'DIMENSION GET GET incremental\n' % (job_name, self.update_every) - self.http_version_chart = 'CHART %s.http_version' \ - ' "" "Requests Per HTTP Version" requests/s "http versions"' \ - ' web_log.http_version stacked 12 %s\n' % (job_name, self.update_every) - - # Remove 'request_time' chart from ORDER if resp_time not in match_dict if 'resp_time' not in match_dict: self.order.remove('response_time') - # Remove 'clients_all' chart from ORDER if specified in the configuration - if not self.all_time: + if 'resp_time_upstream' not in match_dict: + self.order.remove('response_time_upstream') + + if not self.conf.get('all_time', True): self.order.remove('clients_all') + # Add 'detailed_response_codes' chart if specified in the configuration - if self.detailed_response_codes: - self.detailed_chart = list() - for prio, add_to_dim in enumerate(DET_RESP_AGGR): - self.detailed_chart.append('CHART %s.detailed_response_codes%s ""' - ' "Detailed Response Codes %s" requests/s responses' - ' web_log.detailed_response_codes%s stacked %s %s\n' - % (job_name, add_to_dim, add_to_dim[1:], add_to_dim, - str(prio), self.update_every)) - - codes = DET_RESP_AGGR[:1] if self.detailed_response_aggregate else DET_RESP_AGGR[1:] + if self.conf.get('detailed_response_codes', True): + codes = DET_RESP_AGGR[:1] if self.conf.get('detailed_response_aggregate', True) else DET_RESP_AGGR[1:] for code in codes: self.order.append('detailed_response_codes%s' % code) - self.definitions['detailed_response_codes%s' % code] = {'options': - [None, - 'Detailed Response Codes %s' % code[1:], - 'requests/s', - 'responses', - 'web_log.detailed_response_codes%s' % code, - 'stacked'], - 'lines': []} + self.definitions['detailed_response_codes%s' % code] \ + = {'options': [None, 'Detailed Response Codes %s' % code[1:], 'requests/s', 'responses', + 'web_log.detailed_response_codes%s' % code, 'stacked'], + 'lines': []} # Add 'requests_per_url' chart if specified in the configuration - if self.url_pattern: - self.definitions['requests_per_url'] = {'options': [None, 'Requests Per Url', 'requests/s', - 'urls', 'web_log.requests_per_url', 'stacked'], - 'lines': [['rpu_other', 'other', 'incremental']]} - for elem in self.url_pattern: - self.definitions['requests_per_url']['lines'].append([elem.description, elem.description[4:], + if self.storage['url_pattern']: + for elem in self.storage['url_pattern']: + self.definitions['requests_per_url']['lines'].append([elem.description, + elem.description[12:], 'incremental']) - self.data.update({elem.description: 0}) - self.data.update({'rpu_other': 0}) + self.data[elem.description] = 0 + self.data['url_pattern_other'] = 0 else: self.order.remove('requests_per_url') - def add_new_dimension(self, dimension, line_list, chart_string, key): - """ - :param dimension: str: response status code. Ex.: '202', '499' - :param line_list: list: Ex.: ['202', '202', 'incremental'] - :param chart_string: Current string we need to pass to netdata to rebuild the chart - :param key: str: CHARTS dict key (chart name). Ex.: 'response_time' - :return: str: new chart string = previous + new dimensions - """ - self.data.update({dimension: 0}) - # SET method check if dim in _dimensions - self._dimensions.append(dimension) - # UPDATE method do SET only if dim in definitions - self.definitions[key]['lines'].append(line_list) - chart = chart_string - chart += "%s %s\n" % ('DIMENSION', ' '.join(line_list)) - print(chart) - return chart + # Add 'requests_per_user_defined' chart if specified in the configuration + if self.storage['user_pattern'] and 'user_defined' in match_dict: + for elem in self.storage['user_pattern']: + self.definitions['requests_per_user_defined']['lines'].append([elem.description, + elem.description[13:], + 'incremental']) + self.data[elem.description] = 0 + self.data['user_pattern_other'] = 0 + else: + self.order.remove('requests_per_user_defined') - def _get_access_data(self): + def get_data(self, raw_data=None): """ - Parse new log lines + Parses new log lines :return: dict OR None None if _get_raw_data method fails. In all other cases - dict. """ - raw = self._get_raw_data() - if raw is None: - return None + if not raw_data: + return None if raw_data is None else self.data + + filtered_data = self.filter_data(raw_data=raw_data) + + unique_current = set() + timings = defaultdict(lambda: dict(minimum=None, maximum=0, summary=0, count=0)) - request_time, unique_current = list(), list() - request_counter = {'count': 0, 'sum': 0} ip_address_counter = {'unique_cur_ip': 0} - for line in raw: - match = self.regex.search(line) + for line in filtered_data: + match = self.storage['regex'].search(line) if match: match_dict = match.groupdict() try: - code = ''.join([match_dict['code'][0], 'xx']) + code = match_dict['code'][0] + 'xx' self.data[code] += 1 except KeyError: self.data['0xx'] += 1 # detailed response code - if self.detailed_response_codes: - self._get_data_detailed_response_codes(match_dict['code']) + if self.conf.get('detailed_response_codes', True): + self.get_data_per_response_codes_detailed(code=match_dict['code']) # response statuses - self._get_data_statuses(match_dict['code']) + self.get_data_per_statuses(code=match_dict['code']) # requests per url - if self.url_pattern: - self._get_data_per_url(match_dict['url']) + if self.storage['url_pattern']: + self.get_data_per_pattern(row=match_dict['url'], + other='url_pattern_other', + pattern=self.storage['url_pattern']) + # requests per user defined pattern + if self.storage['user_pattern'] and 'user_defined' in match_dict: + self.get_data_per_pattern(row=match_dict['user_defined'], + other='user_pattern_other', + pattern=self.storage['user_pattern']) # requests per http method - self._get_data_http_method(match_dict['method']) + if match_dict['method'] not in self.data: + self.add_new_dimension(dimension_id=match_dict['method'], + chart_key='http_method') + self.data[match_dict['method']] += 1 # requests per http version if 'http_version' in match_dict: - self._get_data_http_version(match_dict['http_version']) + dim_id = match_dict['http_version'].replace('.', '_') + if dim_id not in self.data: + self.add_new_dimension(dimension_id=dim_id, + chart_key='http_version', + dimension=match_dict['http_version']) + self.data[dim_id] += 1 # bandwidth sent bytes_sent = match_dict['bytes_sent'] if '-' not in match_dict['bytes_sent'] else 0 self.data['bytes_sent'] += int(bytes_sent) @@ -487,92 +520,245 @@ class Service(LogService): if 'resp_length' in match_dict: self.data['resp_length'] += int(match_dict['resp_length']) if 'resp_time' in match_dict: - resp_time = self.resp_time_func(float(match_dict['resp_time'])) - bisect.insort_left(request_time, resp_time) - request_counter['count'] += 1 - request_counter['sum'] += resp_time + get_timings(timings=timings['resp_time'], + time=self.storage['func_resp_time'](float(match_dict['resp_time']))) + if 'resp_time_upstream' in match_dict and match_dict['resp_time_upstream'] != '-': + get_timings(timings=timings['resp_time_upstream'], + time=self.storage['func_resp_time'](float(match_dict['resp_time_upstream']))) # requests per ip proto proto = 'ipv4' if '.' in match_dict['address'] else 'ipv6' self.data['req_' + proto] += 1 # unique clients ips - if address_not_in_pool(self.unique_all_time, match_dict['address'], - self.data['unique_tot_ipv4'] + self.data['unique_tot_ipv6']): + if address_not_in_pool(pool=self.storage['unique_all_time'], + address=match_dict['address'], + pool_size=self.data['unique_tot_ipv4'] + self.data['unique_tot_ipv6']): self.data['unique_tot_' + proto] += 1 - if address_not_in_pool(unique_current, match_dict['address'], ip_address_counter['unique_cur_ip']): + if match_dict['address'] not in unique_current: self.data['unique_cur_' + proto] += 1 - ip_address_counter['unique_cur_ip'] += 1 + unique_current.add(match_dict['address']) else: self.data['unmatched'] += 1 # timings - if request_time: - self.data['resp_time_min'] += int(request_time[0]) - self.data['resp_time_avg'] += int(round(float(request_counter['sum']) / request_counter['count'])) - self.data['resp_time_max'] += int(request_time[-1]) + for elem in timings: + self.data[elem + '_min'] += timings[elem]['minimum'] + self.data[elem + '_avg'] += timings[elem]['summary'] / timings[elem]['count'] + self.data[elem + '_max'] += timings[elem]['maximum'] return self.data - def _get_data_detailed_response_codes(self, code): + def find_regex(self, last_line): """ - :param code: str: CODE from parsed line. Ex.: '202, '499' - :return: - Calls add_new_dimension method If the value is found for the first time + :param last_line: str: literally last line from log file + :return: tuple where: + [0]: dict or None: match_dict or None + [1]: str: error description + We need to find appropriate pattern for current log file + All logic is do a regex search through the string for all predefined patterns + until we find something or fail. """ - if code not in self.data: - if self.detailed_response_aggregate: - chart_string_copy = self.detailed_chart[0] - self.detailed_chart[0] = self.add_new_dimension(code, [code, code, 'incremental'], - chart_string_copy, 'detailed_response_codes') - else: - code_index = int(code[0]) if int(code[0]) < 6 else 6 - chart_string_copy = self.detailed_chart[code_index] - chart_name = 'detailed_response_codes' + DET_RESP_AGGR[code_index] - self.detailed_chart[code_index] = self.add_new_dimension(code, [code, code, 'incremental'], - chart_string_copy, chart_name) - self.data[code] += 1 + # REGEX: 1.IPv4 address 2.HTTP method 3. URL 4. Response code + # 5. Bytes sent 6. Response length 7. Response process time + default = re.compile(r'(?P
    [\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+|-)') + + apache_ext_insert = re.compile(r'(?P
    [\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+|-)' + r' (?P\d+)' + r' (?P\d+) ') + + apache_ext_append = re.compile(r'(?P
    [\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+|-)' + r' .*?' + r' (?P\d+)' + r' (?P\d+)' + r'(?: |$)') + + nginx_ext_insert = re.compile(r'(?P
    [\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+)' + r' (?P\d+)' + r' (?P\d+\.\d+) ') + + nginx_ext2_insert = re.compile(r'(?P
    [\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+)' + r' (?P\d+)' + r' (?P\d+\.\d+)' + r' (?P[\d.-]+) ') + + nginx_ext_append = re.compile(r'(?P
    [\da-f.:]+)' + r' -.*?"(?P[A-Z]+)' + r' (?P[^ ]+)' + r' [A-Z]+/(?P\d\.\d)"' + r' (?P[1-9]\d{2})' + r' (?P\d+)' + r' .*?' + r' (?P\d+)' + r' (?P\d+\.\d+)') + + def func_usec(time): + return time + + def func_sec(time): + return time * 1000000 + + r_regex = [apache_ext_insert, apache_ext_append, + nginx_ext2_insert, nginx_ext_insert, nginx_ext_append, + default] + r_function = [func_usec, func_usec, func_sec, func_sec, func_sec, func_usec] + regex_function = zip(r_regex, r_function) + + match_dict = dict() + for regex, func in regex_function: + match = regex.search(last_line) + if match: + self.storage['regex'] = regex + self.storage['func_resp_time'] = func + match_dict = match.groupdict() + break + + return find_regex_return(match_dict=match_dict or None, + msg='Unknown log format. You need to use "custom_log_format" feature.') - def _get_data_http_method(self, method): + def find_regex_custom(self, last_line): """ - :param method: str: METHOD from parsed line. Ex.: 'GET', 'POST' - :return: - Calls add_new_dimension method If the value is found for the first time + :param last_line: str: literally last line from log file + :return: tuple where: + [0]: dict or None: match_dict or None + [1]: str: error description + + We are here only if "custom_log_format" is in logs. We need to make sure: + 1. "custom_log_format" is a dict + 2. "pattern" in "custom_log_format" and pattern is instance + 3. if "time_multiplier" is in "custom_log_format" it must be instance + + If all parameters is ok we need to make sure: + 1. Pattern search is success + 2. Pattern search contains named subgroups (?P) (= "match_dict") + + If pattern search is success we need to make sure: + 1. All mandatory keys ['address', 'code', 'bytes_sent', 'method', 'url'] are in "match_dict" + + If this is True we need to make sure: + 1. All mandatory key values from "match_dict" have the correct format + ("code" is integer, "method" is uppercase word, etc) + + If non mandatory keys in "match_dict" we need to make sure: + 1. All non mandatory key values from match_dict ['resp_length', 'resp_time'] have the correct format + ("resp_length" is integer or "-", "resp_time" is integer or float) + """ - if method not in self.data: - chart_string_copy = self.http_method_chart - self.http_method_chart = self.add_new_dimension(method, [method, method, 'incremental'], - chart_string_copy, 'http_method') - self.data[method] += 1 + if not hasattr(self.conf.get('custom_log_format'), 'keys'): + return find_regex_return(msg='Custom log: "custom_log_format" is not a ') + + pattern = self.conf.get('custom_log_format', dict()).get('pattern') + if not (pattern and isinstance(pattern, str)): + return find_regex_return(msg='Custom log: "pattern" option is not specified or type is not ') + + resp_time_func = self.conf.get('custom_log_format', dict()).get('time_multiplier') or 0 + + if not isinstance(resp_time_func, int): + return find_regex_return(msg='Custom log: "time_multiplier" is not an integer') + + try: + regex = re.compile(pattern) + except re.error as error: + return find_regex_return(msg='Pattern compile error: %s' % str(error)) + match = regex.search(last_line) + if not match: + return find_regex_return(msg='Custom log: pattern search FAILED') - def _get_data_http_version(self, http_version): + match_dict = match.groupdict() or None + if match_dict is None: + return find_regex_return(msg='Custom log: search OK but contains no named subgroups' + ' (you need to use ?P)') + mandatory_dict = {'address': r'[\da-f.:]+', + 'code': r'[1-9]\d{2}', + 'method': r'[A-Z]+', + 'bytes_sent': r'\d+|-'} + optional_dict = {'resp_length': r'\d+', + 'resp_time': r'[\d.]+', + 'resp_time_upstream': r'[\d.-]+', + 'http_version': r'\d\.\d'} + + mandatory_values = set(mandatory_dict) - set(match_dict) + if mandatory_values: + return find_regex_return(msg='Custom log: search OK but some mandatory keys (%s) are missing' + % list(mandatory_values)) + for key in mandatory_dict: + if not re.search(mandatory_dict[key], match_dict[key]): + return find_regex_return(msg='Custom log: can\'t parse "%s": %s' + % (key, match_dict[key])) + + optional_values = set(optional_dict) & set(match_dict) + for key in optional_values: + if not re.search(optional_dict[key], match_dict[key]): + return find_regex_return(msg='Custom log: can\'t parse "%s": %s' + % (key, match_dict[key])) + + dot_in_time = '.' in match_dict.get('resp_time', '') + if dot_in_time: + self.storage['func_resp_time'] = lambda time: time * (resp_time_func or 1000000) + else: + self.storage['func_resp_time'] = lambda time: time * (resp_time_func or 1) + + self.storage['regex'] = regex + return find_regex_return(match_dict=match_dict) + + def get_data_per_response_codes_detailed(self, code): """ - :param http_version: str: METHOD from parsed line. Ex.: '1.1', '1.0' + :param code: str: CODE from parsed line. Ex.: '202, '499' :return: Calls add_new_dimension method If the value is found for the first time """ - http_version_dim_id = http_version.replace('.', '_') - if http_version_dim_id not in self.data: - chart_string_copy = self.http_version_chart - self.http_version_chart = self.add_new_dimension(http_version_dim_id, - [http_version_dim_id, http_version, 'incremental'], - chart_string_copy, 'http_version') - self.data[http_version_dim_id] += 1 - - def _get_data_per_url(self, url): + if code not in self.data: + if self.conf.get('detailed_response_aggregate', True): + self.add_new_dimension(dimension_id=code, + chart_key='detailed_response_codes') + else: + code_index = int(code[0]) if int(code[0]) < 6 else 6 + chart_key = 'detailed_response_codes' + DET_RESP_AGGR[code_index] + self.add_new_dimension(dimension_id=code, + chart_key=chart_key) + self.data[code] += 1 + + def get_data_per_pattern(self, row, other, pattern): """ - :param url: str: URL from parsed line + :param row: str: + :param other: str: + :param pattern: named tuple: (['pattern_description', 'regular expression']) :return: Scan through string looking for the first location where patterns produce a match for all user defined patterns """ match = None - for elem in self.url_pattern: - if elem.pattern.search(url): + for elem in pattern: + if elem.func(row): self.data[elem.description] += 1 match = True break if not match: - self.data['rpu_other'] += 1 + self.data[other] += 1 - def _get_data_statuses(self, code): + def get_data_per_statuses(self, code): """ :param code: str: response status code. Ex.: '202', '499' :return: @@ -590,23 +776,209 @@ class Service(LogService): self.data['other_requests'] += 1 +class ApacheCache: + def __init__(self, configuration): + self.conf = configuration + self.order = ORDER_APACHE_CACHE + self.definitions = CHARTS_APACHE_CACHE + + @staticmethod + def check(): + return True + + @staticmethod + def get_data(raw_data=None): + data = dict(hit=0, miss=0, other=0) + if not raw_data: + return None if raw_data is None else data + + for line in raw_data: + if 'cache hit' in line: + data['hit'] += 1 + elif 'cache miss' in line: + data['miss'] += 1 + else: + data['other'] += 1 + return data + + +class Squid(Mixin): + def __init__(self, configuration): + self.conf = configuration + self.order = ORDER_SQUID + self.definitions = CHARTS_SQUID + self.pre_filter = check_patterns('filter', self.conf.get('filter')) + self.storage = dict() + self.data = {'duration_max': 0, 'duration_avg': 0, 'duration_min': 0, 'bytes': 0, + '0xx': 0, '1xx': 0, '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0, + 'other': 0, 'unmatched': 0, 'unique_ipv4': 0, 'unique_ipv6': 0, + 'unique_tot_ipv4': 0, 'unique_tot_ipv6': 0, 'successful_requests': 0, + 'redirects': 0, 'bad_requests': 0, 'server_errors': 0, 'other_requests': 0 + } + + def check(self): + last_line = self.get_last_line() + if not last_line: + return False + self.storage['unique_all_time'] = list() + self.storage['regex'] = re.compile(r'[0-9.]+\s+(?P[0-9]+)' + r' (?P[\da-f.:]+)' + r' (?P[A-Z_]+)/' + r'(?P[0-9]+)' + r' (?P[0-9]+)' + r' (?P[A-Z_]+)' + r' (?P[^ ]+)' + r' (?P[^ ]+)' + r' (?P[A-Z_]+)/[\da-f.:-]+' + r' (?P[^\n]+)') + + match = self.storage['regex'].search(last_line) + if not match: + self.error('Regex not matches (%s)' % self.storage['regex'].pattern) + return False + self.storage['dynamic'] = { + 'http_code': + {'chart': 'squid_detailed_response_codes', + 'func_dim_id': None, + 'func_dim': None}, + 'hier_code': { + 'chart': 'squid_hier_code', + 'func_dim_id': None, + 'func_dim': lambda v: v.replace('HIER_', '')}, + 'method': { + 'chart': 'squid_method', + 'func_dim_id': None, + 'func_dim': None}, + 'mime_type': { + 'chart': 'squid_mime_type', + 'func_dim_id': lambda v: v.split('/')[0], + 'func_dim': None}} + return True + + def get_data(self, raw_data=None): + if not raw_data: + return None if raw_data is None else self.data + + filtered_data = self.filter_data(raw_data=raw_data) + + unique_ip = set() + timings = defaultdict(lambda: dict(minimum=None, maximum=0, summary=0, count=0)) + + for row in filtered_data: + match = self.storage['regex'].search(row) + if match: + match = match.groupdict() + if match['duration'] != '0': + get_timings(timings=timings['duration'], time=float(match['duration']) * 1000) + try: + self.data[match['http_code'][0] + 'xx'] += 1 + except KeyError: + self.data['other'] += 1 + + self.get_data_per_statuses(match['http_code']) + + self.get_data_per_squid_code(match['squid_code']) + + self.data['bytes'] += int(match['bytes']) + + proto = 'ipv4' if '.' in match['client_address'] else 'ipv6' + # unique clients ips + if address_not_in_pool(pool=self.storage['unique_all_time'], + address=match['client_address'], + pool_size=self.data['unique_tot_ipv4'] + self.data['unique_tot_ipv6']): + self.data['unique_tot_' + proto] += 1 + + if match['client_address'] not in unique_ip: + self.data['unique_' + proto] += 1 + unique_ip.add(match['client_address']) + + for key, values in self.storage['dynamic'].items(): + if match[key] == '-': + continue + dimension_id = values['func_dim_id'](match[key]) if values['func_dim_id'] else match[key] + if dimension_id not in self.data: + dimension = values['func_dim'](match[key]) if values['func_dim'] else dimension_id + self.add_new_dimension(dimension_id=dimension_id, + chart_key=values['chart'], + dimension=dimension) + self.data[dimension_id] += 1 + else: + self.data['unmatched'] += 1 + + for elem in timings: + self.data[elem + '_min'] += timings[elem]['minimum'] + self.data[elem + '_avg'] += timings[elem]['summary'] / timings[elem]['count'] + self.data[elem + '_max'] += timings[elem]['maximum'] + return self.data + + def get_data_per_statuses(self, code): + """ + :param code: str: response status code. Ex.: '202', '499' + :return: + """ + code_class = code[0] + if code_class == '2' or code == '304' or code_class == '1' or code == '000': + self.data['successful_requests'] += 1 + elif code_class == '3': + self.data['redirects'] += 1 + elif code_class == '4': + self.data['bad_requests'] += 1 + elif code_class == '5' or code_class == '6': + self.data['server_errors'] += 1 + else: + self.data['other_requests'] += 1 + + def get_data_per_squid_code(self, code): + """ + :param code: str: squid response code. Ex.: 'TCP_MISS', 'TCP_MISS_ABORTED' + :return: + """ + if code not in self.data: + self.add_new_dimension(dimension_id=code, chart_key='squid_code') + self.data[code] += 1 + if '_' not in code: + return + for tag in code.split('_'): + try: + chart_key = SQUID_CODES[tag] + except KeyError: + continue + if tag not in self.data: + self.add_new_dimension(dimension_id=tag, chart_key=chart_key) + self.data[tag] += 1 + + +def get_timings(timings, time): + """ + :param timings: + :param time: + :return: + """ + if timings['minimum'] is None: + timings['minimum'] = time + if time > timings['maximum']: + timings['maximum'] = time + elif time < timings['minimum']: + timings['minimum'] = time + timings['summary'] += time + timings['count'] += 1 + + def address_not_in_pool(pool, address, pool_size): """ :param pool: list of ip addresses :param address: ip address :param pool_size: current pool size - :return: True if address not in pool. False if address in pool. + :return: True if address not in pool. False otherwise. """ index = bisect.bisect_left(pool, address) if index < pool_size: if pool[index] == address: return False - else: - bisect.insort_left(pool, address) - return True - else: bisect.insort_left(pool, address) return True + bisect.insort_left(pool, address) + return True def find_regex_return(match_dict=None, msg='Generic error message'): @@ -618,36 +990,53 @@ def find_regex_return(match_dict=None, msg='Generic error message'): return match_dict, msg -def check_req_per_url_pattern(string, url_pattern): +def check_patterns(string, dimension_regex_dict): """ :param string: str: - :param url_pattern: dict: ex. {'dim1': 'pattern1>', 'dim2': ''} + :param dimension_regex_dict: dict: ex. {'dim1': '', 'dim2': ''} :return: list of named tuples or None: We need to make sure all patterns are valid regular expressions """ - if not hasattr(url_pattern, 'keys'): + if not hasattr(dimension_regex_dict, 'keys'): return None result = list() - def is_valid_pattern(pattern): + def valid_pattern(pattern): """ :param pattern: str :return: re.compile(pattern) or None """ if not isinstance(pattern, str): return False - else: - try: - compile_pattern = re.compile(pattern) - except re.error: - return False - else: - return compile_pattern + try: + return re.compile(pattern) + except re.error: + return False - for dimension, regex in url_pattern.items(): - valid_pattern = is_valid_pattern(regex) - if isinstance(dimension, str) and valid_pattern: - result.append(NAMED_URL_PATTERN(description='_'.join([string, dimension]), pattern=valid_pattern)) + def func_search(pattern): + def closure(v): + return pattern.search(v) + + return closure + for dimension, regex in dimension_regex_dict.items(): + valid = valid_pattern(regex) + if isinstance(dimension, str) and valid_pattern: + func = func_search(valid) + result.append(NAMED_PATTERN(description='_'.join([string, dimension]), + func=func)) return result or None + + +def find_job_name(override_name, name): + """ + :param override_name: str: 'name' var from configuration file + :param name: str: 'job_name' from configuration file + :return: str: new job name + We need this for dynamic charts. Actually the same logic as in python.d.plugin. + """ + add_to_name = override_name or name + if add_to_name: + return '_'.join(['web_log', re.sub('\s+', '_', add_to_name)]) + return 'web_log' diff --git a/src/Makefile.am b/src/Makefile.am index 1c1dd3385..601d3204f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,6 +44,8 @@ netdata_SOURCES = \ appconfig.h \ avl.c \ avl.h \ + backend_prometheus.c \ + backend_prometheus.h \ backends.c \ backends.h \ clocks.c \ @@ -119,6 +121,10 @@ netdata_SOURCES = \ simple_pattern.h \ socket.c \ socket.h \ + statistical.c \ + statistical.h \ + statsd.c \ + statsd.h \ storage_number.c \ storage_number.h \ sys_devices_system_edac_mc.c \ @@ -146,6 +152,13 @@ netdata_SOURCES += \ plugin_freebsd.c \ plugin_freebsd.h \ freebsd_sysctl.c \ + freebsd_getmntinfo.c \ + freebsd_getifaddrs.c \ + freebsd_devstat.c \ + zfs_common.c \ + zfs_common.h \ + freebsd_kstat_zfs.c \ + freebsd_ipfw.c \ $(NULL) else if MACOS @@ -178,6 +191,9 @@ netdata_SOURCES += \ proc_net_softnet_stat.c \ proc_net_stat_conntrack.c \ proc_net_stat_synproxy.c \ + zfs_common.c \ + zfs_common.h \ + proc_spl_kstat_zfs.c \ proc_stat.c \ proc_sys_kernel_random_entropy_avail.c \ proc_vmstat.c \ diff --git a/src/Makefile.in b/src/Makefile.in index d7229d18a..3ce869b0d 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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. @@ -17,6 +16,51 @@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -43,6 +87,13 @@ plugins_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) @FREEBSD_TRUE@ plugin_freebsd.c \ @FREEBSD_TRUE@ plugin_freebsd.h \ @FREEBSD_TRUE@ freebsd_sysctl.c \ +@FREEBSD_TRUE@ freebsd_getmntinfo.c \ +@FREEBSD_TRUE@ freebsd_getifaddrs.c \ +@FREEBSD_TRUE@ freebsd_devstat.c \ +@FREEBSD_TRUE@ zfs_common.c \ +@FREEBSD_TRUE@ zfs_common.h \ +@FREEBSD_TRUE@ freebsd_kstat_zfs.c \ +@FREEBSD_TRUE@ freebsd_ipfw.c \ @FREEBSD_TRUE@ $(NULL) @FREEBSD_FALSE@@MACOS_TRUE@am__append_4 = \ @@ -74,6 +125,9 @@ plugins_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) @FREEBSD_FALSE@@MACOS_FALSE@ proc_net_softnet_stat.c \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_net_stat_conntrack.c \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_net_stat_synproxy.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ zfs_common.c \ +@FREEBSD_FALSE@@MACOS_FALSE@ zfs_common.h \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_spl_kstat_zfs.c \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_stat.c \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_sys_kernel_random_entropy_avail.c \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_vmstat.c \ @@ -86,9 +140,9 @@ plugins_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) @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 +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(dist_cache_DATA) $(dist_log_DATA) \ + $(dist_registry_DATA) $(dist_varlib_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 $(top_srcdir)/m4/ax_c_lto.m4 \ @@ -127,8 +181,9 @@ 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 \ + backend_prometheus.c backend_prometheus.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 \ @@ -144,25 +199,34 @@ am__netdata_SOURCES_DIST = adaptive_resortable_list.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 \ + statistical.c statistical.h statsd.c statsd.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 \ + freebsd_sysctl.c freebsd_getmntinfo.c freebsd_getifaddrs.c \ + freebsd_devstat.c zfs_common.c zfs_common.h \ + freebsd_kstat_zfs.c freebsd_ipfw.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 \ proc_diskstats.c proc_interrupts.c proc_softirqs.c \ proc_loadavg.c proc_meminfo.c proc_net_dev.c \ proc_net_ip_vs_stats.c proc_net_netstat.c proc_net_rpc_nfs.c \ proc_net_rpc_nfsd.c proc_net_snmp.c proc_net_snmp6.c \ proc_net_softnet_stat.c proc_net_stat_conntrack.c \ - proc_net_stat_synproxy.c proc_stat.c \ + proc_net_stat_synproxy.c proc_spl_kstat_zfs.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_2 = plugin_freebsd.$(OBJEXT) \ -@FREEBSD_TRUE@ freebsd_sysctl.$(OBJEXT) +@FREEBSD_TRUE@ freebsd_sysctl.$(OBJEXT) \ +@FREEBSD_TRUE@ freebsd_getmntinfo.$(OBJEXT) \ +@FREEBSD_TRUE@ freebsd_getifaddrs.$(OBJEXT) \ +@FREEBSD_TRUE@ freebsd_devstat.$(OBJEXT) zfs_common.$(OBJEXT) \ +@FREEBSD_TRUE@ freebsd_kstat_zfs.$(OBJEXT) \ +@FREEBSD_TRUE@ freebsd_ipfw.$(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) \ @@ -185,15 +249,17 @@ am__netdata_SOURCES_DIST = adaptive_resortable_list.c \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_net_softnet_stat.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_net_stat_conntrack.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_net_stat_synproxy.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ zfs_common.$(OBJEXT) \ +@FREEBSD_FALSE@@MACOS_FALSE@ proc_spl_kstat_zfs.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_stat.$(OBJEXT) \ @FREEBSD_FALSE@@MACOS_FALSE@ proc_sys_kernel_random_entropy_avail.$(OBJEXT) \ @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 = adaptive_resortable_list.$(OBJEXT) \ - appconfig.$(OBJEXT) avl.$(OBJEXT) backends.$(OBJEXT) \ - clocks.$(OBJEXT) common.$(OBJEXT) daemon.$(OBJEXT) \ - dictionary.$(OBJEXT) eval.$(OBJEXT) \ + appconfig.$(OBJEXT) avl.$(OBJEXT) backend_prometheus.$(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) \ @@ -210,6 +276,7 @@ am_netdata_OBJECTS = adaptive_resortable_list.$(OBJEXT) \ rrddimvar.$(OBJEXT) rrdfamily.$(OBJEXT) rrdhost.$(OBJEXT) \ rrdpush.$(OBJEXT) rrdset.$(OBJEXT) rrdsetvar.$(OBJEXT) \ rrdvar.$(OBJEXT) simple_pattern.$(OBJEXT) socket.$(OBJEXT) \ + statistical.$(OBJEXT) statsd.$(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) \ @@ -220,18 +287,43 @@ am_netdata_OBJECTS = adaptive_resortable_list.$(OBJEXT) \ netdata_OBJECTS = $(am_netdata_OBJECTS) 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) $(freeipmi_plugin_SOURCES) \ $(netdata_SOURCES) DIST_SOURCES = $(am__apps_plugin_SOURCES_DIST) \ $(freeipmi_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 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -261,11 +353,29 @@ 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 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -437,8 +547,9 @@ dist_registry_DATA = .keep dist_log_DATA = .keep 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 \ + backend_prometheus.c backend_prometheus.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 \ @@ -454,7 +565,8 @@ netdata_SOURCES = adaptive_resortable_list.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 \ + statistical.c statistical.h statsd.c statsd.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 \ @@ -525,14 +637,18 @@ $(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 } \ @@ -553,7 +669,8 @@ 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 @@ -562,14 +679,18 @@ 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 } \ @@ -590,22 +711,26 @@ 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) - $(LINK) $(apps_plugin_OBJECTS) $(apps_plugin_LDADD) $(LIBS) + $(AM_V_CCLD)$(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) + $(AM_V_CCLD)$(LINK) $(freeipmi_plugin_OBJECTS) $(freeipmi_plugin_LDADD) $(LIBS) + netdata$(EXEEXT): $(netdata_OBJECTS) $(netdata_DEPENDENCIES) $(EXTRA_netdata_DEPENDENCIES) @rm -f netdata$(EXEEXT) - $(LINK) $(netdata_OBJECTS) $(netdata_LDADD) $(LIBS) + $(AM_V_CCLD)$(LINK) $(netdata_OBJECTS) $(netdata_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -617,12 +742,18 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/appconfig.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apps_plugin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/avl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_prometheus.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backends.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clocks.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dictionary.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freebsd_devstat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freebsd_getifaddrs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freebsd_getmntinfo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freebsd_ipfw.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freebsd_kstat_zfs.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@ @@ -662,6 +793,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_stat_synproxy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_self_mountinfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_softirqs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_spl_kstat_zfs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_stat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_sys_kernel_random_entropy_avail.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_uptime.Po@am__quote@ @@ -690,6 +822,8 @@ distclean-compile: @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)/statistical.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statsd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage_number.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sys_devices_system_edac_mc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sys_devices_system_node.Po@am__quote@ @@ -703,24 +837,28 @@ distclean-compile: @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@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zfs_common.Po@am__quote@ .c.o: -@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@ +@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@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: -@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@ +@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@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(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"; \ @@ -737,8 +875,11 @@ 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"; \ @@ -755,8 +896,11 @@ 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"; \ @@ -773,8 +917,11 @@ 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"; \ @@ -790,26 +937,15 @@ uninstall-dist_varlibDATA: files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(varlibdir)'; $(am__uninstall_files_from_dir) -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) +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ - 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; }; }'`; \ + $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ @@ -821,15 +957,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $$unique; \ fi; \ fi -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; }; }'`; \ +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique @@ -838,6 +970,21 @@ 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 @@ -986,20 +1133,20 @@ uninstall-am: uninstall-dist_cacheDATA uninstall-dist_logDATA \ .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 \ +.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-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 uninstall \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-dist_cacheDATA uninstall-dist_logDATA \ uninstall-dist_registryDATA uninstall-dist_varlibDATA \ uninstall-pluginsPROGRAMS uninstall-sbinPROGRAMS diff --git a/src/appconfig.c b/src/appconfig.c index 71ff4b75e..91c4c5c54 100644 --- a/src/appconfig.c +++ b/src/appconfig.c @@ -135,6 +135,7 @@ static inline struct section *appconfig_section_create(struct config *root, cons struct section *co = callocz(1, sizeof(struct section)); co->name = strdupz(section); co->hash = simple_hash(co->name); + netdata_mutex_init(&co->mutex); avl_init_lock(&co->values_index, appconfig_option_compare); @@ -213,7 +214,8 @@ int appconfig_move(struct config *root, const char *section_old, const char *nam if(!co_new) co_new = appconfig_section_create(root, section_new); config_section_wrlock(co_old); - config_section_wrlock(co_new); + if(co_old != co_new) + config_section_wrlock(co_new); cv_old = appconfig_option_index_find(co_old, name_old, 0); if(!cv_old) goto cleanup; @@ -250,7 +252,8 @@ int appconfig_move(struct config *root, const char *section_old, const char *nam ret = 0; cleanup: - config_section_unlock(co_new); + if(co_old != co_new) + config_section_unlock(co_new); config_section_unlock(co_old); return ret; } @@ -294,6 +297,17 @@ long long appconfig_get_number(struct config *root, const char *section, const c return strtoll(s, NULL, 0); } +long double appconfig_get_float(struct config *root, const char *section, const char *name, long double value) +{ + char buffer[100], *s; + sprintf(buffer, "%0.5Lf", value); + + s = appconfig_get(root, section, name, buffer); + if(!s) return value; + + return str2ld(s, NULL); +} + int appconfig_get_boolean(struct config *root, const char *section, const char *name, int value) { char *s; @@ -337,7 +351,7 @@ const char *appconfig_set_default(struct config *root, const char *section, cons { struct config_option *cv; - debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value); + debug(D_CONFIG, "request to set default config in section '%s', name '%s', value '%s'", section, name, value); struct section *co = appconfig_section_find(root, section); if(!co) return appconfig_set(root, section, name, value); @@ -393,6 +407,16 @@ long long appconfig_set_number(struct config *root, const char *section, const c return value; } +long double appconfig_set_float(struct config *root, const char *section, const char *name, long double value) +{ + char buffer[100]; + sprintf(buffer, "%0.5Lf", value); + + appconfig_set(root, section, name, buffer); + + return value; +} + int appconfig_set_boolean(struct config *root, const char *section, const char *name, int value) { char *s; @@ -417,11 +441,11 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used) if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME; - debug(D_CONFIG, "Opening config file '%s'", filename); + debug(D_CONFIG, "CONFIG: opening config file '%s'", filename); FILE *fp = fopen(filename, "r"); if(!fp) { - error("Cannot open file '%s'", filename); + error("CONFIG: cannot open file '%s'", filename); return 0; } @@ -430,8 +454,8 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used) line++; s = trim(buffer); - if(!s) { - debug(D_CONFIG, "Ignoring line %d, it is empty.", line); + if(!s || *s == '#') { + debug(D_CONFIG, "CONFIG: ignoring line %d of file '%s', it is empty.", line, filename); continue; } @@ -449,14 +473,14 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used) if(!co) { // line outside a section - error("Ignoring line %d ('%s'), it is outside all sections.", line, s); + error("CONFIG: ignoring line %d ('%s') of file '%s', it is outside all sections.", line, s, filename); continue; } char *name = s; char *value = strchr(s, '='); if(!value) { - error("Ignoring line %d ('%s'), there is no = in it.", line, s); + error("CONFIG: ignoring line %d ('%s') of file '%s', there is no = in it.", line, s, filename); continue; } *value = '\0'; @@ -465,12 +489,12 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used) name = trim(name); value = trim(value); - if(!name) { - error("Ignoring line %d, name is empty.", line); + if(!name || *name == '#') { + error("CONFIG: ignoring line %d of file '%s', name is empty.", line, filename); continue; } if(!value) { - debug(D_CONFIG, "Ignoring line %d, value is empty.", line); + debug(D_CONFIG, "CONFIG: ignoring line %d of file '%s', value is empty.", line, filename); continue; } @@ -479,12 +503,12 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used) 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); + debug(D_CONFIG, "CONFIG: line %d of file '%s', overwriting '%s/%s'.", line, filename, co->name, cv->name); freez(cv->value); cv->value = strdupz(value); } else - debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name); + debug(D_CONFIG, "CONFIG: ignoring line %d of file '%s', '%s/%s' is already present and used.", line, filename, co->name, cv->name); } cv->flags |= CONFIG_VALUE_LOADED; } @@ -531,6 +555,7 @@ void appconfig_generate(struct config *root, BUFFER *wb, int only_changed) 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_STATSD) || !strcmp(co->name, CONFIG_SECTION_PLUGINS) || !strcmp(co->name, CONFIG_SECTION_REGISTRY) || !strcmp(co->name, CONFIG_SECTION_HEALTH) @@ -558,7 +583,7 @@ void appconfig_generate(struct config *root, BUFFER *wb, int only_changed) if(only_changed && !changed) continue; if(!used) { - buffer_sprintf(wb, "\n# node '%s' is not used.", co->name); + buffer_sprintf(wb, "\n# section '%s' is not used.", co->name); } buffer_sprintf(wb, "\n[%s]\n", co->name); diff --git a/src/appconfig.h b/src/appconfig.h index 45cc8cfd5..b8c2ee80c 100644 --- a/src/appconfig.h +++ b/src/appconfig.h @@ -5,6 +5,7 @@ #define CONFIG_SECTION_GLOBAL "global" #define CONFIG_SECTION_WEB "web" +#define CONFIG_SECTION_STATSD "statsd" #define CONFIG_SECTION_PLUGINS "plugins" #define CONFIG_SECTION_REGISTRY "registry" #define CONFIG_SECTION_HEALTH "health" @@ -34,12 +35,14 @@ extern int appconfig_load(struct config *root, char *filename, int overwrite_use 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 long double appconfig_get_float(struct config *root, const char *section, const char *name, long double 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 long double appconfig_set_float(struct config *root, const char *section, const char *name, long double 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); @@ -53,12 +56,14 @@ extern void appconfig_generate(struct config *root, BUFFER *wb, int only_changed #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_float(section, name, value) appconfig_get_float(&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) -#define config_set(section, name, default_value) appconfig_get(&netdata_config, section, name, default_value) +#define config_set(section, name, default_value) appconfig_set(&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_float(section, name, value) appconfig_set_float(&netdata_config, section, name, value) #define config_set_boolean(section, name, value) appconfig_set_boolean(&netdata_config, section, name, value) #define config_exists(section, name) appconfig_exists(&netdata_config, section, name) diff --git a/src/apps_plugin.c b/src/apps_plugin.c index b1bf06bee..ecb6aaeac 100644 --- a/src/apps_plugin.c +++ b/src/apps_plugin.c @@ -34,8 +34,7 @@ #define MAX_COMPARE_NAME 100 #define MAX_NAME 100 -#define MAX_CMDLINE 1024 - +#define MAX_CMDLINE 16384 // ---------------------------------------------------------------------------- // the rates we are going to send to netdata will have this detail a value of: @@ -109,11 +108,12 @@ static size_t // metric. // the total system time, as reported by /proc/stat +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) static kernel_uint_t global_utime = 0, global_stime = 0, global_gtime = 0; - +#endif // the normalization ratios, as calculated by normalize_utilization() double utime_fix_ratio = 1.0, @@ -127,7 +127,6 @@ double utime_fix_ratio = 1.0, cminflt_fix_ratio = 1.0, cmajflt_fix_ratio = 1.0; - // ---------------------------------------------------------------------------- // target // @@ -223,7 +222,7 @@ size_t struct pid_stat { int32_t pid; char comm[MAX_COMPARE_NAME + 1]; - char cmdline[MAX_CMDLINE + 1]; + char *cmdline; uint32_t log_thrown; @@ -438,7 +437,7 @@ static struct target *get_users_target(uid_t uid) { w->idhash = simple_hash(w->id); struct passwd *pw = getpwuid(uid); - if(!pw) + if(!pw || !pw->pw_name || !*pw->pw_name) snprintfz(w->name, MAX_NAME, "%u", uid); else snprintfz(w->name, MAX_NAME, "%s", pw->pw_name); @@ -471,7 +470,7 @@ struct target *get_groups_target(gid_t gid) w->idhash = simple_hash(w->id); struct group *gr = getgrgid(gid); - if(!gr) + if(!gr || !gr->gr_name || !*gr->gr_name) snprintfz(w->name, MAX_NAME, "%u", gid); else snprintfz(w->name, MAX_NAME, "%s", gr->gr_name); @@ -698,6 +697,7 @@ static inline void del_pid_entry(pid_t pid) { freez(p->statm_filename); freez(p->io_filename); freez(p->cmdline_filename); + freez(p->cmdline); freez(p); all_pids[pid] = NULL; @@ -768,7 +768,7 @@ static inline void assign_target_to_pid(struct pid_stat *p) { 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)) + || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && p->cmdline && strstr(p->cmdline, w->compare)) ))) { if(w->target) p->target = w->target; @@ -787,6 +787,7 @@ static inline void assign_target_to_pid(struct pid_stat *p) { // update pids from proc static inline int read_proc_pid_cmdline(struct pid_stat *p) { + static char cmdline[MAX_CMDLINE + 1]; #ifdef __FreeBSD__ size_t i, bytes = MAX_CMDLINE; @@ -796,7 +797,7 @@ static inline int read_proc_pid_cmdline(struct pid_stat *p) { mib[1] = KERN_PROC; mib[2] = KERN_PROC_ARGS; mib[3] = p->pid; - if (unlikely(sysctl(mib, 4, p->cmdline, &bytes, NULL, 0))) + if (unlikely(sysctl(mib, 4, cmdline, &bytes, NULL, 0))) goto cleanup; #else if(unlikely(!p->cmdline_filename)) { @@ -808,15 +809,17 @@ static inline int read_proc_pid_cmdline(struct pid_stat *p) { int fd = open(p->cmdline_filename, O_RDONLY, 0666); if(unlikely(fd == -1)) goto cleanup; - ssize_t i, bytes = read(fd, p->cmdline, MAX_CMDLINE); + ssize_t i, bytes = read(fd, cmdline, MAX_CMDLINE); close(fd); if(unlikely(bytes < 0)) goto cleanup; #endif - p->cmdline[bytes] = '\0'; + cmdline[bytes] = '\0'; for(i = 0; i < bytes ; i++) - if(unlikely(!p->cmdline[i])) p->cmdline[i] = ' '; + if(unlikely(!cmdline[i])) cmdline[i] = ' '; + + p->cmdline = strdupz(cmdline); if(unlikely(debug)) fprintf(stderr, "Read file '%s' contents: %s\n", p->cmdline_filename, p->cmdline); @@ -825,7 +828,7 @@ static inline int read_proc_pid_cmdline(struct pid_stat *p) { cleanup: // copy the command to the command line - strncpyz(p->cmdline, p->comm, MAX_CMDLINE); + p->cmdline = strdupz(p->comm); return 0; } @@ -1157,24 +1160,13 @@ cleanup: #endif } +#if (ALL_PIDS_ARE_READ_INSTANTLY == 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 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", netdata_configured_host_prefix); ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT); @@ -1183,7 +1175,6 @@ static inline int read_proc_stat() { ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; -#endif last_collected_usec = collected_usec; collected_usec = now_monotonic_usec(); @@ -1193,25 +1184,13 @@ static inline int read_proc_stat() { // temporary - it is added global_ntime; kernel_uint_t global_ntime = 0; -#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 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; @@ -1224,7 +1203,6 @@ static inline int read_proc_stat() { // 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; @@ -1240,7 +1218,11 @@ cleanup: global_gtime = 0; return 0; } - +#else +static inline int read_proc_stat() { + return 0; +} +#endif // ---------------------------------------------------------------------------- @@ -1751,7 +1733,6 @@ static inline int print_process_and_parents(struct pid_stat *p, usec_t time) { } static inline void print_process_tree(struct pid_stat *p, char *msg) { - log_date(stderr); fprintf(stderr, "%s: process %s (%d, %s) with parents:\n", msg, p->comm, p->pid, p->updated?"running":"exited"); print_process_and_parents(p, p->stat_collected_usec); } @@ -1860,7 +1841,6 @@ static inline void process_exited_processes() { continue; if(unlikely(debug)) { - log_date(stderr); 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 @@ -2620,14 +2600,13 @@ static inline void send_END(void) { fprintf(stdout, "END\n"); } -static usec_t send_resource_usage_to_netdata() { +void send_resource_usage_to_netdata(usec_t dt) { static struct timeval last = { 0, 0 }; static struct rusage me_last; struct timeval now; struct rusage me; - usec_t usec; usec_t cpuuser; usec_t cpusyst; @@ -2635,10 +2614,6 @@ static usec_t send_resource_usage_to_netdata() { now_monotonic_timeval(&last); getrusage(RUSAGE_SELF, &me_last); - // the first time, give a zero to allow - // netdata calibrate to the current time - // usec = update_every * USEC_PER_SEC; - usec = 0ULL; cpuuser = 0; cpusyst = 0; } @@ -2646,7 +2621,6 @@ static usec_t send_resource_usage_to_netdata() { now_monotonic_timeval(&now); getrusage(RUSAGE_SELF, &me); - usec = dt_usec(&now, &last); cpuuser = me.ru_utime.tv_sec * USEC_PER_SEC + me.ru_utime.tv_usec; cpusyst = me.ru_stime.tv_sec * USEC_PER_SEC + me.ru_stime.tv_usec; @@ -2658,38 +2632,45 @@ static usec_t send_resource_usage_to_netdata() { 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" + 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" + , update_every + ); + +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) + fprintf(stdout, + "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" + 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 ); +#endif + } fprintf(stdout, @@ -2705,31 +2686,35 @@ static usec_t send_resource_usage_to_netdata() { "SET targets = %zu\n" "SET new_pids = %zu\n" "END\n" - "BEGIN netdata.apps_fix %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 + , dt , cpuuser , cpusyst - , usec + , dt , calls_counter , file_counter , all_pids_count , all_files_len , apps_groups_targets_count , targets_assignment_counter - , usec - , (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 (ALL_PIDS_ARE_READ_INSTANTLY == 0) + fprintf(stdout, + "BEGIN netdata.apps_fix %llu\n" + "SET utime = %u\n" + "SET stime = %u\n" + "SET gtime = %u\n" + "SET minflt = %u\n" + "SET majflt = %u\n" + "END\n" + , dt + , (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" @@ -2739,17 +2724,17 @@ static usec_t send_resource_usage_to_netdata() { "SET cminflt = %u\n" "SET cmajflt = %u\n" "END\n" - , usec + , dt , (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; +#endif } +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) static void normalize_utilization(struct target *root) { struct target *w; @@ -2895,25 +2880,30 @@ static void normalize_utilization(struct target *root) { ); } } +#else // ALL_PIDS_ARE_READ_INSTANTLY == 1 +static void normalize_utilization(struct target *root) { + (void)root; +} +#endif // ALL_PIDS_ARE_READ_INSTANTLY -static void send_collected_data_to_netdata(struct target *root, const char *type, usec_t usec) { +static void send_collected_data_to_netdata(struct target *root, const char *type, usec_t dt) { struct target *w; - send_BEGIN(type, "cpu", usec); + send_BEGIN(type, "cpu", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) 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); + send_BEGIN(type, "cpu_user", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) 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); + send_BEGIN(type, "cpu_system", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, (kernel_uint_t)(w->stime * stime_fix_ratio) + (include_exited_childs?((kernel_uint_t)(w->cstime * cstime_fix_ratio)):0ULL)); @@ -2921,7 +2911,7 @@ static void send_collected_data_to_netdata(struct target *root, const char *type send_END(); if(show_guest_time) { - send_BEGIN(type, "cpu_guest", usec); + send_BEGIN(type, "cpu_guest", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, (kernel_uint_t)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((kernel_uint_t)(w->cgtime * cgtime_fix_ratio)):0ULL)); @@ -2929,42 +2919,42 @@ static void send_collected_data_to_netdata(struct target *root, const char *type send_END(); } - send_BEGIN(type, "threads", usec); + send_BEGIN(type, "threads", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, w->num_threads); } send_END(); - send_BEGIN(type, "processes", usec); + send_BEGIN(type, "processes", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, w->processes); } send_END(); - send_BEGIN(type, "mem", usec); + send_BEGIN(type, "mem", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, (w->statm_resident > w->statm_share)?(w->statm_resident - w->statm_share):0ULL); } send_END(); - send_BEGIN(type, "vmem", usec); + send_BEGIN(type, "vmem", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, w->statm_size); } send_END(); - send_BEGIN(type, "minor_faults", usec); + send_BEGIN(type, "minor_faults", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) 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); + send_BEGIN(type, "major_faults", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, (kernel_uint_t)(w->majflt * majflt_fix_ratio) + (include_exited_childs?((kernel_uint_t)(w->cmajflt * cmajflt_fix_ratio)):0ULL)); @@ -2972,14 +2962,14 @@ static void send_collected_data_to_netdata(struct target *root, const char *type send_END(); #ifndef __FreeBSD__ - send_BEGIN(type, "lreads", usec); + send_BEGIN(type, "lreads", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, w->io_logical_bytes_read); } send_END(); - send_BEGIN(type, "lwrites", usec); + send_BEGIN(type, "lwrites", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, w->io_logical_bytes_written); @@ -2987,14 +2977,14 @@ static void send_collected_data_to_netdata(struct target *root, const char *type send_END(); #endif - send_BEGIN(type, "preads", usec); + send_BEGIN(type, "preads", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, w->io_storage_bytes_read); } send_END(); - send_BEGIN(type, "pwrites", usec); + send_BEGIN(type, "pwrites", dt); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) send_SET(w->name, w->io_storage_bytes_written); @@ -3002,21 +2992,21 @@ static void send_collected_data_to_netdata(struct target *root, const char *type send_END(); if(enable_file_charts) { - send_BEGIN(type, "files", usec); + send_BEGIN(type, "files", dt); for (w = root; w; w = w->next) { if (unlikely(w->exposed)) send_SET(w->name, w->openfiles); } send_END(); - send_BEGIN(type, "sockets", usec); + send_BEGIN(type, "sockets", dt); for (w = root; w; w = w->next) { if (unlikely(w->exposed)) send_SET(w->name, w->opensockets); } send_END(); - send_BEGIN(type, "pipes", usec); + send_BEGIN(type, "pipes", dt); for (w = root; w; w = w->next) { if (unlikely(w->exposed)) send_SET(w->name, w->openpipes); @@ -3472,8 +3462,9 @@ int main(int argc, char **argv) { static int profiling_count=0; profiling_count++; if(unlikely(profiling_count > 1000)) exit(0); + usec_t dt = update_every * USEC_PER_SEC; #else - heartbeat_next(&hb, step); + usec_t dt = heartbeat_next(&hb, step); #endif if(!collect_data_for_all_processes()) { @@ -3485,7 +3476,7 @@ int main(int argc, char **argv) { calculate_netdata_statistics(); normalize_utilization(apps_groups_root_target); - usec_t dt = send_resource_usage_to_netdata(); + send_resource_usage_to_netdata(dt); // this is smart enough to show only newly added apps, when needed send_charts_updates_to_netdata(apps_groups_root_target, "apps", "Apps"); diff --git a/src/backend_prometheus.c b/src/backend_prometheus.c new file mode 100644 index 000000000..88ec2c65b --- /dev/null +++ b/src/backend_prometheus.c @@ -0,0 +1,397 @@ +#include "common.h" + +// ---------------------------------------------------------------------------- +// PROMETHEUS +// /api/v1/allmetrics?format=prometheus + +static struct prometheus_server { + const char *server; + uint32_t hash; + time_t last_access; + struct prometheus_server *next; +} *prometheus_server_root = NULL; + +static inline time_t prometheus_server_last_access(const char *server, time_t now) { + uint32_t hash = simple_hash(server); + + struct prometheus_server *ps; + for(ps = prometheus_server_root; ps ;ps = ps->next) { + if (hash == ps->hash && !strcmp(server, ps->server)) { + time_t last = ps->last_access; + ps->last_access = now; + return last; + } + } + + ps = callocz(1, sizeof(struct prometheus_server)); + ps->server = strdupz(server); + ps->hash = hash; + ps->last_access = now; + ps->next = prometheus_server_root; + prometheus_server_root = ps; + + return 0; +} + +static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable) { + size_t n; + + for(n = 0; *s && n < usable ; d++, s++, n++) { + register char c = *s; + + if(!isalnum(c)) *d = '_'; + else *d = c; + } + *d = '\0'; + + return n; +} + +static inline size_t prometheus_label_copy(char *d, const char *s, size_t usable) { + size_t n; + + // make sure we can escape one character without overflowing the buffer + usable--; + + for(n = 0; *s && n < usable ; d++, s++, n++) { + register char c = *s; + + if(unlikely(c == '"' || c == '\\' || c == '\n')) { + *d++ = '\\'; + n++; + } + *d = c; + } + *d = '\0'; + + return n; +} + +static inline char *prometheus_units_copy(char *d, const char *s, size_t usable) { + const char *sorig = s; + char *ret = d; + size_t n; + + *d++ = '_'; + for(n = 1; *s && n < usable ; d++, s++, n++) { + register char c = *s; + + if(!isalnum(c)) *d = '_'; + else *d = c; + } + + if(n == 2 && sorig[0] == '%') { + n = 0; + d = ret; + s = "_percent"; + for( ; *s && n < usable ; n++) *d++ = *s++; + } + else if(n > 3 && sorig[n-3] == '/' && sorig[n-2] == 's') { + n = n - 2; + d -= 2; + s = "_persec"; + for( ; *s && n < usable ; n++) *d++ = *s++; + } + + *d = '\0'; + + return ret; +} + + +#define PROMETHEUS_ELEMENT_MAX 256 +#define PROMETHEUS_LABELS_MAX 1024 + +static void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb, const char *prefix, uint32_t options, time_t after, time_t before, int allhosts, int help, int types, int names) { + rrdhost_rdlock(host); + + char hostname[PROMETHEUS_ELEMENT_MAX + 1]; + prometheus_label_copy(hostname, host->hostname, PROMETHEUS_ELEMENT_MAX); + + char labels[PROMETHEUS_LABELS_MAX + 1] = ""; + if(allhosts) { + if(host->tags && *(host->tags)) + buffer_sprintf(wb, "netdata_host_tags{instance=\"%s\",%s} 1 %llu\n", hostname, host->tags, now_realtime_usec() / USEC_PER_MS); + + snprintfz(labels, PROMETHEUS_LABELS_MAX, ",instance=\"%s\"", hostname); + } + else { + if(host->tags && *(host->tags)) + buffer_sprintf(wb, "netdata_host_tags{%s} 1 %llu\n", host->tags, now_realtime_usec() / USEC_PER_MS); + } + + // for each chart + RRDSET *st; + rrdset_foreach_read(st, host) { + char chart[PROMETHEUS_ELEMENT_MAX + 1]; + char context[PROMETHEUS_ELEMENT_MAX + 1]; + char family[PROMETHEUS_ELEMENT_MAX + 1]; + char units[PROMETHEUS_ELEMENT_MAX + 1] = ""; + + prometheus_label_copy(chart, (names && st->name)?st->name:st->id, PROMETHEUS_ELEMENT_MAX); + prometheus_label_copy(family, st->family, PROMETHEUS_ELEMENT_MAX); + prometheus_name_copy(context, st->context, PROMETHEUS_ELEMENT_MAX); + + if(likely(backends_can_send_rrdset(options, st))) { + rrdset_rdlock(st); + + int as_collected = ((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED); + int homogeneus = 1; + if(as_collected) { + if(rrdset_flag_check(st, RRDSET_FLAG_HOMEGENEOUS_CHECK)) + rrdset_update_heterogeneous_flag(st); + + if(rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) + homogeneus = 0; + } + else { + if((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AVERAGE) + prometheus_units_copy(units, st->units, PROMETHEUS_ELEMENT_MAX); + } + + if(unlikely(help)) + buffer_sprintf(wb, "\n# COMMENT %s chart \"%s\", context \"%s\", family \"%s\", units \"%s\"\n" + , (homogeneus)?"homogeneus":"heterogeneous" + , (names && st->name) ? st->name : st->id + , st->context + , st->family + , st->units + ); + + // for each dimension + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if(rd->collections_counter) { + char dimension[PROMETHEUS_ELEMENT_MAX + 1]; + char *suffix = ""; + + if (as_collected) { + // we need as-collected / raw data + + const char *t = "gauge", *h = "gives"; + if(rd->algorithm == RRD_ALGORITHM_INCREMENTAL || + rd->algorithm == RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL) { + t = "counter"; + h = "delta gives"; + suffix = "_total"; + } + + if(homogeneus) { + // all the dimensions of the chart, has the same algorithm, multiplier and divisor + // we add all dimensions as labels + + prometheus_label_copy(dimension, (names && rd->name) ? rd->name : rd->id, PROMETHEUS_ELEMENT_MAX); + + if(unlikely(help)) + buffer_sprintf(wb + , "# COMMENT %s_%s%s: chart \"%s\", context \"%s\", family \"%s\", dimension \"%s\", value * " COLLECTED_NUMBER_FORMAT " / " COLLECTED_NUMBER_FORMAT " %s %s (%s)\n" + , prefix + , context + , suffix + , (names && st->name) ? st->name : st->id + , st->context + , st->family + , (names && rd->name) ? rd->name : rd->id + , rd->multiplier + , rd->divisor + , h + , st->units + , t + ); + + if(unlikely(types)) + buffer_sprintf(wb, "# COMMENT TYPE %s_%s%s %s\n" + , prefix + , context + , suffix + , t + ); + + buffer_sprintf(wb + , "%s_%s%s{chart=\"%s\",family=\"%s\",dimension=\"%s\"%s} " COLLECTED_NUMBER_FORMAT " %llu\n" + , prefix + , context + , suffix + , chart + , family + , dimension + , labels + , rd->last_collected_value + , timeval_msec(&rd->last_collected_time) + ); + } + else { + // the dimensions of the chart, do not have the same algorithm, multiplier or divisor + // we create a metric per dimension + + prometheus_name_copy(dimension, (names && rd->name) ? rd->name : rd->id, PROMETHEUS_ELEMENT_MAX); + + if(unlikely(help)) + buffer_sprintf(wb + , "# COMMENT %s_%s_%s%s: chart \"%s\", context \"%s\", family \"%s\", dimension \"%s\", value * " COLLECTED_NUMBER_FORMAT " / " COLLECTED_NUMBER_FORMAT " %s %s (%s)\n" + , prefix + , context + , dimension + , suffix + , (names && st->name) ? st->name : st->id + , st->context + , st->family + , (names && rd->name) ? rd->name : rd->id + , rd->multiplier + , rd->divisor + , h + , st->units + , t + ); + + if(unlikely(types)) + buffer_sprintf(wb, "# COMMENT TYPE %s_%s_%s%s %s\n" + , prefix + , context + , dimension + , suffix + , t + ); + + buffer_sprintf(wb + , "%s_%s_%s%s{chart=\"%s\",family=\"%s\"%s} " COLLECTED_NUMBER_FORMAT " %llu\n" + , prefix + , context + , dimension + , suffix + , chart + , family + , labels + , rd->last_collected_value + , timeval_msec(&rd->last_collected_time) + ); + } + } + else { + // we need average or sum of the data + + time_t first_t = after, last_t = before; + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options, &first_t, &last_t); + + if(!isnan(value) && !isinf(value)) { + + if((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AVERAGE) + suffix = "_average"; + else if((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_SUM) + suffix = "_sum"; + + prometheus_label_copy(dimension, (names && rd->name) ? rd->name : rd->id, PROMETHEUS_ELEMENT_MAX); + + if (unlikely(help)) + buffer_sprintf(wb, "# COMMENT %s_%s%s%s: dimension \"%s\", value is %s, gauge, dt %llu to %llu inclusive\n" + , prefix + , context + , units + , suffix + , (names && rd->name) ? rd->name : rd->id + , st->units + , (unsigned long long)first_t + , (unsigned long long)last_t + ); + + if (unlikely(types)) + buffer_sprintf(wb, "# COMMENT TYPE %s_%s%s%s gauge\n" + , prefix + , context + , units + , suffix + ); + + buffer_sprintf(wb, "%s_%s%s%s{chart=\"%s\",family=\"%s\",dimension=\"%s\"%s} " CALCULATED_NUMBER_FORMAT " %llu\n" + , prefix + , context + , units + , suffix + , chart + , family + , dimension + , labels + , value + , last_t * MSEC_PER_SEC + ); + } + } + } + } + + rrdset_unlock(st); + } + } + + rrdhost_unlock(host); +} + +static inline time_t prometheus_preparation(RRDHOST *host, BUFFER *wb, uint32_t options, const char *server, time_t now, int help) { + if(!server || !*server) server = "default"; + + time_t after = prometheus_server_last_access(server, now); + + int first_seen = 0; + if(!after) { + after = now - backend_update_every; + first_seen = 1; + } + + if(after > now) { + // oops! this should never happen + after = now - backend_update_every; + } + + if(help) { + int show_range = 1; + char *mode; + if((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED) { + mode = "as collected"; + show_range = 0; + } + else if((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AVERAGE) + mode = "average"; + else if((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_SUM) + mode = "sum"; + else + mode = "unknown"; + + buffer_sprintf(wb, "# COMMENT netdata \"%s\" to %sprometheus \"%s\", source \"%s\", last seen %lu %s" + , host->hostname + , (first_seen)?"FIRST SEEN ":"" + , server + , mode + , (unsigned long)((first_seen)?0:(now - after)) + , (first_seen)?"never":"seconds ago" + ); + + if(show_range) + buffer_sprintf(wb, ", time range %lu to %lu", (unsigned long)after, (unsigned long)now); + + buffer_strcat(wb, "\n\n"); + } + + return after; +} + +void rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(RRDHOST *host, BUFFER *wb, const char *server, const char *prefix, uint32_t options, int help, int types, int names) { + time_t before = now_realtime_sec(); + + // we start at the point we had stopped before + time_t after = prometheus_preparation(host, wb, options, server, before, help); + + rrd_stats_api_v1_charts_allmetrics_prometheus(host, wb, prefix, options, after, before, 0, help, types, names); +} + +void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(RRDHOST *host, BUFFER *wb, const char *server, const char *prefix, uint32_t options, int help, int types, int names) { + time_t before = now_realtime_sec(); + + // we start at the point we had stopped before + time_t after = prometheus_preparation(host, wb, options, server, before, help); + + rrd_rdlock(); + rrdhost_foreach_read(host) { + rrd_stats_api_v1_charts_allmetrics_prometheus(host, wb, prefix, options, after, before, 1, help, types, names); + } + rrd_unlock(); +} diff --git a/src/backend_prometheus.h b/src/backend_prometheus.h new file mode 100644 index 000000000..53dddb0d2 --- /dev/null +++ b/src/backend_prometheus.h @@ -0,0 +1,11 @@ +// +// Created by costa on 09/07/17. +// + +#ifndef NETDATA_BACKEND_PROMETHEUS_H +#define NETDATA_BACKEND_PROMETHEUS_H + +extern void rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(RRDHOST *host, BUFFER *wb, const char *server, const char *prefix, uint32_t options, int help, int types, int names); +extern void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(RRDHOST *host, BUFFER *wb, const char *server, const char *prefix, uint32_t options, int help, int types, int names); + +#endif //NETDATA_BACKEND_PROMETHEUS_H diff --git a/src/backends.c b/src/backends.c index 3e385cab5..eed4ab409 100644 --- a/src/backends.c +++ b/src/backends.c @@ -22,47 +22,81 @@ // 5. repeats the above forever. // -#define BACKEND_SOURCE_DATA_AS_COLLECTED 0x00000001 -#define BACKEND_SOURCE_DATA_AVERAGE 0x00000002 -#define BACKEND_SOURCE_DATA_SUM 0x00000004 - +const char *backend_prefix = "netdata"; +int backend_send_names = 1; +int backend_update_every = 10; +uint32_t backend_options = BACKEND_SOURCE_DATA_AVERAGE; // ---------------------------------------------------------------------------- // helper functions for backends +static inline size_t backend_name_copy(char *d, const char *s, size_t usable) { + size_t n; + + for(n = 0; *s && n < usable ; d++, s++, n++) { + char c = *s; + + if(c != '.' && !isalnum(c)) *d = '_'; + else *d = c; + } + *d = '\0'; + + return n; +} + // 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( +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 + , time_t *first_timestamp // the first point of the database used in this response + , time_t *last_timestamp // the timestamp that should be reported to backend ) { // 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 update_every = st->update_every; - if(unlikely(before < first_t || after > last_t)) - // the chart has not been updated in the wanted timeframe - return NAN; + // step back a little, to make sure we have complete data collection + // for all metrics + after -= update_every * 2; + before -= update_every * 2; // align the time-frame - // for 'after' also skip the first value by adding st->update_every - after = after - after % st->update_every + st->update_every; - before = before - before % st->update_every; + after = after - (after % update_every); + before = before - (before % update_every); - if(unlikely(after < first_t)) - after = first_t; + // for before, loose another iteration + // the latest point will be reported the next time + before -= update_every; if(unlikely(after > before)) - // this can happen when st->update_every > before - after - before = after; + // this can happen when update_every > before - after + after = before; + + if(unlikely(after < first_t)) + after = first_t; if(unlikely(before > last_t)) before = last_t; + if(unlikely(before < first_t || after > last_t)) { + // the chart has not been updated in the wanted timeframe + debug(D_BACKEND, "BACKEND: %s.%s.%s: aligned timeframe %lu to %lu is outside the chart's database range %lu to %lu", + st->rrdhost->hostname, st->id, rd->id, + (unsigned long)after, (unsigned long)before, + (unsigned long)first_t, (unsigned long)last_t + ); + return NAN; + } + + *first_timestamp = after; + *last_timestamp = before; + size_t counter = 0; calculated_number sum = 0; @@ -88,10 +122,15 @@ static inline calculated_number backend_calculate_value_from_stored_data( counter++; } - if(unlikely(!counter)) + if(unlikely(!counter)) { + debug(D_BACKEND, "BACKEND: %s.%s.%s: no values stored in database for range %lu to %lu", + st->rrdhost->hostname, st->id, rd->id, + (unsigned long)after, (unsigned long)before + ); return NAN; + } - if(unlikely(options & BACKEND_SOURCE_DATA_SUM)) + if(unlikely((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_SUM)) return sum; return sum / (calculated_number)counter; @@ -113,7 +152,7 @@ static inline int discard_response(BUFFER *b, const char *backend) { } *d = '\0'; - info("Received %zu bytes from %s backend. Ignoring them. Sample: '%s'", buffer_strlen(b), backend, sample); + info("BACKEND: received %zu bytes from %s backend. Ignoring them. Sample: '%s'", buffer_strlen(b), backend, sample); buffer_flush(b); return 0; } @@ -138,13 +177,18 @@ static inline int format_dimension_collected_graphite_plaintext( (void)before; (void)options; + char chart_name[RRD_ID_LENGTH_MAX + 1]; + char dimension_name[RRD_ID_LENGTH_MAX + 1]; + backend_name_copy(chart_name, (backend_send_names && st->name)?st->name:st->id, RRD_ID_LENGTH_MAX); + backend_name_copy(dimension_name, (backend_send_names && rd->name)?rd->name:rd->id, RRD_ID_LENGTH_MAX); + buffer_sprintf( b , "%s.%s.%s.%s " COLLECTED_NUMBER_FORMAT " %u\n" , prefix , hostname - , st->id - , rd->id + , chart_name + , dimension_name , rd->last_collected_value , (uint32_t)rd->last_collected_time.tv_sec ); @@ -165,7 +209,13 @@ static inline int format_dimension_stored_graphite_plaintext( ) { (void)host; - calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options); + char chart_name[RRD_ID_LENGTH_MAX + 1]; + char dimension_name[RRD_ID_LENGTH_MAX + 1]; + backend_name_copy(chart_name, (backend_send_names && st->name)?st->name:st->id, RRD_ID_LENGTH_MAX); + backend_name_copy(dimension_name, (backend_send_names && rd->name)?rd->name:rd->id, RRD_ID_LENGTH_MAX); + + time_t first_t = after, last_t = before; + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options, &first_t, &last_t); if(!isnan(value)) { @@ -174,10 +224,10 @@ static inline int format_dimension_stored_graphite_plaintext( , "%s.%s.%s.%s " CALCULATED_NUMBER_FORMAT " %u\n" , prefix , hostname - , st->id - , rd->id + , chart_name + , dimension_name , value - , (uint32_t) before + , (uint32_t) last_t ); return 1; @@ -209,15 +259,22 @@ static inline int format_dimension_collected_opentsdb_telnet( (void)before; (void)options; + char chart_name[RRD_ID_LENGTH_MAX + 1]; + char dimension_name[RRD_ID_LENGTH_MAX + 1]; + backend_name_copy(chart_name, (backend_send_names && st->name)?st->name:st->id, RRD_ID_LENGTH_MAX); + backend_name_copy(dimension_name, (backend_send_names && rd->name)?rd->name:rd->id, RRD_ID_LENGTH_MAX); + buffer_sprintf( b - , "put %s.%s.%s %u " COLLECTED_NUMBER_FORMAT " host=%s\n" + , "put %s.%s.%s %u " COLLECTED_NUMBER_FORMAT " host=%s%s%s\n" , prefix - , st->id - , rd->id + , chart_name + , dimension_name , (uint32_t)rd->last_collected_time.tv_sec , rd->last_collected_value , hostname + , (host->tags)?" ":"" + , (host->tags)?host->tags:"" ); return 1; @@ -236,19 +293,27 @@ static inline int format_dimension_stored_opentsdb_telnet( ) { (void)host; - calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options); + time_t first_t = after, last_t = before; + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options, &first_t, &last_t); + + char chart_name[RRD_ID_LENGTH_MAX + 1]; + char dimension_name[RRD_ID_LENGTH_MAX + 1]; + backend_name_copy(chart_name, (backend_send_names && st->name)?st->name:st->id, RRD_ID_LENGTH_MAX); + backend_name_copy(dimension_name, (backend_send_names && rd->name)?rd->name:rd->id, RRD_ID_LENGTH_MAX); if(!isnan(value)) { buffer_sprintf( b - , "put %s.%s.%s %u " CALCULATED_NUMBER_FORMAT " host=%s\n" + , "put %s.%s.%s %u " CALCULATED_NUMBER_FORMAT " host=%s%s%s\n" , prefix - , st->id - , rd->id - , (uint32_t) before + , chart_name + , dimension_name + , (uint32_t) last_t , value , hostname + , (host->tags)?" ":"" + , (host->tags)?host->tags:"" ); return 1; @@ -329,7 +394,8 @@ static inline int format_dimension_stored_json_plaintext( ) { (void)host; - calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options); + time_t first_t = after, last_t = before; + calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, options, &first_t, &last_t); if(!isnan(value)) { buffer_sprintf(b, "{" @@ -362,7 +428,7 @@ static inline int format_dimension_stored_json_plaintext( rd->name, value, - (uint32_t)before + (uint32_t) last_t ); return 1; @@ -378,6 +444,56 @@ static inline int process_json_response(BUFFER *b) { // ---------------------------------------------------------------------------- // the backend thread +static SIMPLE_PATTERN *charts_pattern = NULL; + +inline int backends_can_send_rrdset(uint32_t options, RRDSET *st) { + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_BACKEND_IGNORE))) + return 0; + + if(unlikely(!rrdset_flag_check(st, RRDSET_FLAG_BACKEND_SEND))) { + // we have not checked this chart + if(simple_pattern_matches(charts_pattern, st->id) || simple_pattern_matches(charts_pattern, st->name)) + rrdset_flag_set(st, RRDSET_FLAG_BACKEND_SEND); + else { + rrdset_flag_set(st, RRDSET_FLAG_BACKEND_IGNORE); + debug(D_BACKEND, "BACKEND: not sending chart '%s' of host '%s', because it is disabled for backends.", st->id, st->rrdhost->hostname); + return 0; + } + } + + if(unlikely(!rrdset_is_available_for_backends(st))) { + debug(D_BACKEND, "BACKEND: not sending chart '%s' of host '%s', because it is not available for backends.", st->id, st->rrdhost->hostname); + return 0; + } + + if(unlikely(st->rrd_memory_mode == RRD_MEMORY_MODE_NONE && !((options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED))) { + debug(D_BACKEND, "BACKEND: not sending chart '%s' of host '%s' because its memory mode is '%s' and the backend requires database access.", st->id, st->rrdhost->hostname, rrd_memory_mode_name(st->rrdhost->rrd_memory_mode)); + return 0; + } + + return 1; +} + +inline uint32_t backend_parse_data_source(const char *source, uint32_t mode) { + if(!strcmp(source, "raw") || !strcmp(source, "as collected") || !strcmp(source, "as-collected") || !strcmp(source, "as_collected") || !strcmp(source, "ascollected")) { + mode |= BACKEND_SOURCE_DATA_AS_COLLECTED; + mode &= ~(BACKEND_SOURCE_BITS ^ BACKEND_SOURCE_DATA_AS_COLLECTED); + } + else if(!strcmp(source, "average")) { + mode |= BACKEND_SOURCE_DATA_AVERAGE; + mode &= ~(BACKEND_SOURCE_BITS ^ BACKEND_SOURCE_DATA_AVERAGE); + } + else if(!strcmp(source, "sum") || !strcmp(source, "volume")) { + mode |= BACKEND_SOURCE_DATA_SUM; + mode &= ~(BACKEND_SOURCE_BITS ^ BACKEND_SOURCE_DATA_SUM); + } + else { + error("BACKEND: invalid data source method '%s'.", source); + } + + return mode; +} + void *backends_main(void *ptr) { int default_port = 0; int sock = -1; @@ -387,13 +503,13 @@ void *backends_main(void *ptr) { 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()); + info("BACKEND: thread created with task id %d", gettid()); if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) - error("Cannot set pthread cancel type to DEFERRED."); + error("BACKEND: cannot set pthread cancel type to DEFERRED."); if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) - error("Cannot set pthread cancel state to ENABLE."); + error("BACKEND: cannot set pthread cancel state to ENABLE."); // ------------------------------------------------------------------------ // collect configuration options @@ -402,45 +518,35 @@ void *backends_main(void *ptr) { .tv_sec = 0, .tv_usec = 0 }; - uint32_t options; 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"); + backend_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); + backend_update_every = (int)config_get_number(CONFIG_SECTION_BACKEND, "update every", backend_update_every); 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); + long timeoutms = config_get_number(CONFIG_SECTION_BACKEND, "timeout ms", backend_update_every * 2 * 1000); + backend_send_names = config_get_boolean(CONFIG_SECTION_BACKEND, "send names instead of ids", backend_send_names); + + charts_pattern = simple_pattern_create(config_get(CONFIG_SECTION_BACKEND, "send charts matching", "*"), SIMPLE_PATTERN_EXACT); + // ------------------------------------------------------------------------ // validate configuration options // and prepare for sending data to our backend - if(!enabled || frequency < 1) - goto cleanup; - - if(!strcmp(source, "as collected")) { - options = BACKEND_SOURCE_DATA_AS_COLLECTED; - } - else if(!strcmp(source, "average")) { - options = BACKEND_SOURCE_DATA_AVERAGE; - } - else if(!strcmp(source, "sum") || !strcmp(source, "volume")) { - options = BACKEND_SOURCE_DATA_SUM; - } - else { - error("Invalid data source method '%s' for backend given. Disabling backed.", source); - goto cleanup; - } + backend_options = backend_parse_data_source(source, backend_options); if(timeoutms < 1) { - error("BACKED invalid timeout %ld ms given. Assuming %d ms.", timeoutms, frequency * 2 * 1000); - timeoutms = frequency * 2 * 1000; + error("BACKEND: invalid timeout %ld ms given. Assuming %d ms.", timeoutms, backend_update_every * 2 * 1000); + timeoutms = backend_update_every * 2 * 1000; } timeout.tv_sec = (timeoutms * 1000) / 1000000; timeout.tv_usec = (timeoutms * 1000) % 1000000; + if(!enabled || backend_update_every < 1) + goto cleanup; // ------------------------------------------------------------------------ // select the backend type @@ -450,7 +556,7 @@ void *backends_main(void *ptr) { default_port = 2003; backend_response_checker = process_graphite_response; - if(options == BACKEND_SOURCE_DATA_AS_COLLECTED) + if((backend_options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED) backend_request_formatter = format_dimension_collected_graphite_plaintext; else backend_request_formatter = format_dimension_stored_graphite_plaintext; @@ -461,7 +567,7 @@ void *backends_main(void *ptr) { default_port = 4242; backend_response_checker = process_opentsdb_response; - if(options == BACKEND_SOURCE_DATA_AS_COLLECTED) + if((backend_options & BACKEND_SOURCE_BITS) == BACKEND_SOURCE_DATA_AS_COLLECTED) backend_request_formatter = format_dimension_collected_opentsdb_telnet; else backend_request_formatter = format_dimension_stored_opentsdb_telnet; @@ -472,19 +578,19 @@ void *backends_main(void *ptr) { default_port = 5448; backend_response_checker = process_json_response; - if (options == BACKEND_SOURCE_DATA_AS_COLLECTED) + if ((backend_options & BACKEND_SOURCE_BITS) == 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); + error("BACKEND: Unknown backend type '%s'", type); goto cleanup; } if(backend_request_formatter == NULL || backend_response_checker == NULL) { - error("backend is misconfigured - disabling it."); + error("BACKEND: backend is misconfigured - disabling it."); goto cleanup; } @@ -509,18 +615,18 @@ void *backends_main(void *ptr) { chart_backend_reconnects = 0, chart_backend_latency = 0; - RRDSET *chart_metrics = rrdset_create_localhost("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", 130600, frequency, RRDSET_TYPE_LINE); + RRDSET *chart_metrics = rrdset_create_localhost("netdata", "backend_metrics", NULL, "backend", NULL, "Netdata Buffered Metrics", "metrics", 130600, backend_update_every, 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_create_localhost("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", 130610, frequency, RRDSET_TYPE_AREA); + RRDSET *chart_bytes = rrdset_create_localhost("netdata", "backend_bytes", NULL, "backend", NULL, "Netdata Backend Data Size", "KB", 130610, backend_update_every, 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_create_localhost("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", 130630, frequency, RRDSET_TYPE_LINE); + RRDSET *chart_ops = rrdset_create_localhost("netdata", "backend_ops", NULL, "backend", NULL, "Netdata Backend Operations", "operations", 130630, backend_update_every, 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); @@ -534,11 +640,11 @@ 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_create_localhost("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", 130620, frequency, RRDSET_TYPE_AREA); + RRDSET *chart_latency = rrdset_create_localhost("netdata", "backend_latency", NULL, "backend", NULL, "Netdata Backend Latency", "ms", 130620, backend_update_every, RRDSET_TYPE_AREA); rrddim_add(chart_latency, "latency", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); */ - 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); + RRDSET *chart_rusage = rrdset_create_localhost("netdata", "backend_thread_cpu", NULL, "backend", NULL, "NetData Backend Thread CPU usage", "milliseconds/s", 130630, backend_update_every, 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); @@ -546,9 +652,9 @@ void *backends_main(void *ptr) { // ------------------------------------------------------------------------ // prepare the backend main loop - info("BACKEND configured ('%s' on '%s' sending '%s' data, every %d seconds, as host '%s', with prefix '%s')", type, destination, source, frequency, hostname, prefix); + info("BACKEND: configured ('%s' on '%s' sending '%s' data, every %d seconds, as host '%s', with prefix '%s')", type, destination, source, backend_update_every, hostname, backend_prefix); - usec_t step_ut = frequency * USEC_PER_SEC; + usec_t step_ut = backend_update_every * USEC_PER_SEC; time_t after = now_realtime_sec(); int failures = 0; heartbeat_t hb; @@ -558,9 +664,10 @@ void *backends_main(void *ptr) { // ------------------------------------------------------------------------ // Wait for the next iteration point. + heartbeat_next(&hb, step_ut); time_t before = now_realtime_sec(); - + debug(D_BACKEND, "BACKEND: preparing buffer for timeframe %lu to %lu", (unsigned long)after, (unsigned long)before); // ------------------------------------------------------------------------ // add to the buffer the data we need to send to the backend @@ -568,33 +675,59 @@ void *backends_main(void *ptr) { int pthreadoldcancelstate; if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 0)) - error("Cannot set pthread cancel state to DISABLE."); + error("BACKEND: cannot set pthread cancel state to DISABLE."); + + size_t count_hosts = 0; + size_t count_charts_total = 0; + size_t count_dims_total = 0; rrd_rdlock(); RRDHOST *host; rrdhost_foreach_read(host) { - if(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE) - continue; - rrdhost_rdlock(host); + count_hosts++; + size_t count_charts = 0; + size_t count_dims = 0; + size_t count_dims_skipped = 0; + + const char *__hostname = (host == localhost)?hostname:host->hostname; + RRDSET *st; rrdset_foreach_read(st, host) { - rrdset_rdlock(st); + if(likely(backends_can_send_rrdset(backend_options, st))) { + rrdset_rdlock(st); + + count_charts++; + + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + if (likely(rd->last_collected_time.tv_sec >= after)) { + chart_buffered_metrics += backend_request_formatter(b, backend_prefix, host, __hostname, st, rd, after, before, backend_options); + count_dims++; + } + else { + debug(D_BACKEND, "BACKEND: not sending dimension '%s' of chart '%s' from host '%s', its last data collection (%lu) is not within our timeframe (%lu to %lu)", rd->id, st->id, __hostname, (unsigned long)rd->last_collected_time.tv_sec, (unsigned long)after, (unsigned long)before); + count_dims_skipped++; + } + } - 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); } - rrdset_unlock(st); } + + debug(D_BACKEND, "BACKEND: sending host '%s', metrics of %zu dimensions, of %zu charts. Skipped %zu dimensions.", __hostname, count_dims, count_charts, count_dims_skipped); + count_charts_total += count_charts; + count_dims_total += count_dims; + rrdhost_unlock(host); } rrd_unlock(); + debug(D_BACKEND, "BACKEND: buffer has %zu bytes, added metrics for %zu dimensions, of %zu charts, from %zu hosts", buffer_strlen(b), count_dims_total, count_charts_total, count_hosts); + if(unlikely(pthread_setcancelstate(pthreadoldcancelstate, NULL) != 0)) - error("Cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate); + error("BACKEND: cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate); // ------------------------------------------------------------------------ @@ -639,14 +772,14 @@ void *backends_main(void *ptr) { chart_receptions++; } else if(r == 0) { - error("Backend '%s' closed the socket", destination); + error("BACKEND: '%s' closed the socket", destination); close(sock); sock = -1; } else { // failed to receive data if(errno != EAGAIN && errno != EWOULDBLOCK) { - error("Cannot receive data from backend '%s'.", destination); + error("BACKEND: cannot receive data from backend '%s'.", destination); } } } @@ -698,7 +831,7 @@ void *backends_main(void *ptr) { } else { // oops! we couldn't send (all or some of the) data - error("Failed to write data to database backend '%s'. Willing to write %zu bytes, wrote %zd bytes. Will re-connect.", destination, len, written); + error("BACKEND: failed to write data to database backend '%s'. Willing to write %zu bytes, wrote %zd bytes. Will re-connect.", destination, len, written); chart_transmission_failures++; if(written != -1) @@ -713,7 +846,7 @@ void *backends_main(void *ptr) { } } else { - error("Failed to update database backend '%s'", destination); + error("BACKEND: failed to update database backend '%s'", destination); chart_transmission_failures++; // increment the counter we check for data loss @@ -723,7 +856,7 @@ void *backends_main(void *ptr) { if(failures > buffer_on_failures) { // too bad! we are going to lose data chart_lost_bytes += buffer_strlen(b); - error("Reached %d backend failures. Flushing buffers to protect this host - this results in data loss on back-end server '%s'", failures, destination); + error("BACKEND: reached %d backend failures. Flushing buffers to protect this host - this results in data loss on back-end server '%s'", failures, destination); buffer_flush(b); failures = 0; chart_data_lost_events++; @@ -781,7 +914,7 @@ cleanup: buffer_free(b); buffer_free(response); - info("BACKEND thread exiting"); + info("BACKEND: thread exiting"); static_thread->enabled = 0; pthread_exit(NULL); diff --git a/src/backends.h b/src/backends.h index 61122a1d0..e882f3db1 100644 --- a/src/backends.h +++ b/src/backends.h @@ -1,6 +1,30 @@ #ifndef NETDATA_BACKENDS_H #define NETDATA_BACKENDS_H 1 -void *backends_main(void *ptr); +#define BACKEND_SOURCE_DATA_AS_COLLECTED 0x00000001 +#define BACKEND_SOURCE_DATA_AVERAGE 0x00000002 +#define BACKEND_SOURCE_DATA_SUM 0x00000004 + +#define BACKEND_SOURCE_BITS (BACKEND_SOURCE_DATA_AS_COLLECTED|BACKEND_SOURCE_DATA_AVERAGE|BACKEND_SOURCE_DATA_SUM) + +extern int backend_send_names; +extern int backend_update_every; +extern uint32_t backend_options; +extern const char *backend_prefix; + +extern void *backends_main(void *ptr); + +extern int backends_can_send_rrdset(uint32_t options, RRDSET *st); +extern uint32_t backend_parse_data_source(const char *source, uint32_t mode); + +extern 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 + , time_t *first_timestamp // the timestamp of the first point used in this response + , time_t *last_timestamp // the timestamp that should be reported to backend +); #endif /* NETDATA_BACKENDS_H */ diff --git a/src/clocks.c b/src/clocks.c index 879ebf911..8f2aa740e 100644 --- a/src/clocks.c +++ b/src/clocks.c @@ -115,8 +115,8 @@ usec_t heartbeat_next(heartbeat_t *hb, usec_t tick) 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); + if(unlikely(dt >= tick + tick / 2)) + error("heartbeat missed %llu microseconds", dt - tick); return dt; } else { diff --git a/src/clocks.h b/src/clocks.h index 197b5431f..ca5715254 100644 --- a/src/clocks.h +++ b/src/clocks.h @@ -55,6 +55,8 @@ typedef usec_t heartbeat_t; #define USEC_PER_SEC 1000000ULL #define MSEC_PER_SEC 1000ULL +#define USEC_PER_MS 1000ULL + #ifndef HAVE_CLOCK_GETTIME /* Fallback function for POSIX.1-2001 clock_gettime() function. * diff --git a/src/common.c b/src/common.c index 88fcf85bc..aa75c198d 100644 --- a/src/common.c +++ b/src/common.c @@ -226,6 +226,13 @@ void json_escape_string(char *dst, const char *src, size_t size) { *d = '\0'; } +void json_fix_string(char *s) { + for( ; *s ;s++) { + if(unlikely(*s == '\\')) *s = '/'; + else if(unlikely(*s == '"')) *s = '\''; + } +} + int sleep_usec(usec_t usec) { #ifndef NETDATA_WITH_USLEEP @@ -895,9 +902,8 @@ char *mystrsep(char **ptr, char *s) { char *trim(char *s) { // skip leading spaces - // and 'comments' as well!? while (*s && isspace(*s)) s++; - if (!*s || *s == '#') return NULL; + if (!*s) return NULL; // skip tailing spaces // this way is way faster. Writes only one NUL char. @@ -913,105 +919,163 @@ char *trim(char *s) { return 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; -#endif - void *mem = NULL; +inline char *trim_all(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 int memory_file_open(const char *filename, size_t size) { + // info("memory_file_open('%s', %zu", filename, size); - errno = 0; int fd = open(filename, O_RDWR | O_CREAT | O_NOATIME, 0664); if (fd != -1) { if (lseek(fd, size, SEEK_SET) == (off_t) size) { if (write(fd, "", 1) == 1) { if (ftruncate(fd, size)) error("Cannot truncate file '%s' to size %zu. Will use the larger file.", filename, size); + } + else error("Cannot write to file '%s' at position %zu.", filename, size); + } + else error("Cannot seek file '%s' to size %zu.", filename, size); + } + else error("Cannot create/open file '%s'.", filename); -#ifdef MADV_MERGEABLE - if (flags & MAP_SHARED || !enable_ksm || !ksm) { -#endif - mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0); - if (mem == MAP_FAILED) { - error("Cannot allocate SHARED memory for file '%s'.", filename); - mem = NULL; - } - else { + return fd; +} + +// mmap_shared is used for memory mode = map +static void *memory_file_mmap(const char *filename, size_t size, int flags) { + // info("memory_file_mmap('%s', %zu", filename, size); + static int log_madvise = 1; + + int fd = -1; + if(filename) { + fd = memory_file_open(filename, size); + if(fd == -1) return MAP_FAILED; + } + + void *mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0); + if (mem != MAP_FAILED) { #ifdef NETDATA_LOG_ALLOCATIONS - mmap_accounting(size); + mmap_accounting(size); #endif - int advise = MADV_SEQUENTIAL | MADV_DONTFORK; - if (flags & MAP_SHARED) advise |= MADV_WILLNEED; - - if (madvise(mem, size, advise) != 0 && log_madvise_1) { - error("Cannot advise the kernel about the memory usage of file '%s'.", filename); - log_madvise_1--; - } - } + int advise = MADV_SEQUENTIAL | MADV_DONTFORK; + if (flags & MAP_SHARED) advise |= MADV_WILLNEED; + + if (madvise(mem, size, advise) != 0 && log_madvise) { + error("Cannot advise the kernel about shared memory usage."); + log_madvise--; + } + } + + if(fd != -1) + close(fd); + + return mem; +} + #ifdef MADV_MERGEABLE - } - else { -/* - // test - load the file into memory - mem = calloc(1, size); - if(mem) { - if(lseek(fd, 0, SEEK_SET) == 0) { - if(read(fd, mem, size) != (ssize_t)size) - error("Cannot read from file '%s'", filename); - } - else - error("Cannot seek to beginning of file '%s'.", filename); - } -*/ - mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0); - if (mem == MAP_FAILED) { - error("Cannot allocate PRIVATE ANONYMOUS memory for KSM for file '%s'.", filename); - mem = NULL; - } - else { +static void *memory_file_mmap_ksm(const char *filename, size_t size, int flags) { + // info("memory_file_mmap_ksm('%s', %zu", filename, size); + static int log_madvise_2 = 1, log_madvise_3 = 1; + + int fd = -1; + if(filename) { + fd = memory_file_open(filename, size); + if(fd == -1) return MAP_FAILED; + } + + void *mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0); + if (mem != MAP_FAILED) { #ifdef NETDATA_LOG_ALLOCATIONS - mmap_accounting(size); -#endif - if (lseek(fd, 0, SEEK_SET) == 0) { - if (read(fd, mem, size) != (ssize_t) size) - error("Cannot read from file '%s'", filename); - } else - error("Cannot seek to beginning of file '%s'.", filename); - - // don't use MADV_SEQUENTIAL|MADV_DONTFORK, they disable MADV_MERGEABLE - if (madvise(mem, size, MADV_SEQUENTIAL | MADV_DONTFORK) != 0 && log_madvise_2) { - error("Cannot advise the kernel about the memory usage (MADV_SEQUENTIAL|MADV_DONTFORK) of file '%s'.", - filename); - log_madvise_2--; - } - - if (madvise(mem, size, MADV_MERGEABLE) != 0 && log_madvise_3) { - error("Cannot advise the kernel about the memory usage (MADV_MERGEABLE) of file '%s'.", - filename); - log_madvise_3--; - } - } - } + mmap_accounting(size); #endif + if(fd != -1) { + if (lseek(fd, 0, SEEK_SET) == 0) { + if (read(fd, mem, size) != (ssize_t) size) + error("Cannot read from file '%s'", filename); } - else - error("Cannot write to file '%s' at position %zu.", filename, size); + else error("Cannot seek to beginning of file '%s'.", filename); } - else - error("Cannot seek file '%s' to size %zu.", filename, size); - close(fd); + // don't use MADV_SEQUENTIAL|MADV_DONTFORK, they disable MADV_MERGEABLE + if (madvise(mem, size, MADV_SEQUENTIAL | MADV_DONTFORK) != 0 && log_madvise_2) { + error("Cannot advise the kernel about the memory usage (MADV_SEQUENTIAL|MADV_DONTFORK) of file '%s'.", filename); + log_madvise_2--; + } + + if (madvise(mem, size, MADV_MERGEABLE) != 0 && log_madvise_3) { + error("Cannot advise the kernel about the memory usage (MADV_MERGEABLE) of file '%s'.", filename); + log_madvise_3--; + } } + + if(fd != -1) + close(fd); + + return mem; +} +#else +static void *memory_file_mmap_ksm(const char *filename, size_t size, int flags) { + // info("memory_file_mmap_ksm FALLBACK ('%s', %zu", filename, size); + + if(filename) + return memory_file_mmap(filename, size, flags); + + // when KSM is not available and no filename is given (memory mode = ram), + // we just report failure + return MAP_FAILED; +} +#endif + +void *mymmap(const char *filename, size_t size, int flags, int ksm) { + void *mem = NULL; + + if (filename && (flags & MAP_SHARED || !enable_ksm || !ksm)) + // memory mode = map | save + // when KSM is not enabled + // MAP_SHARED is used for memory mode = map (no KSM possible) + mem = memory_file_mmap(filename, size, flags); + else - error("Cannot create/open file '%s'.", filename); + // memory mode = save | ram + // when KSM is enabled + // for memory mode = ram, the filename is NULL + mem = memory_file_mmap_ksm(filename, size, flags); + if(mem == MAP_FAILED) return NULL; + + errno = 0; return mem; } -int savememory(const char *filename, void *mem, size_t size) { +int memory_file_save(const char *filename, void *mem, size_t size) { char tmpfilename[FILENAME_MAX + 1]; snprintfz(tmpfilename, FILENAME_MAX, "%s.%ld.tmp", filename, (long) getpid()); @@ -1228,3 +1292,47 @@ unsigned long end_tsc(void) { return (((unsigned long)d << 32) | (unsigned long)a) - tsc; } */ + +int recursively_delete_dir(const char *path, const char *reason) { + DIR *dir = opendir(path); + if(!dir) { + error("Cannot read %s directory to be deleted '%s'", reason?reason:"", path); + return -1; + } + + int ret = 0; + struct dirent *de = NULL; + while((de = readdir(dir))) { + if(de->d_type == DT_DIR + && ( + (de->d_name[0] == '.' && de->d_name[1] == '\0') + || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0') + )) + continue; + + char fullpath[FILENAME_MAX + 1]; + snprintfz(fullpath, FILENAME_MAX, "%s/%s", path, de->d_name); + + if(de->d_type == DT_DIR) { + int r = recursively_delete_dir(fullpath, reason); + if(r > 0) ret += r; + continue; + } + + info("Deleting %s file '%s'", reason?reason:"", fullpath); + if(unlikely(unlink(fullpath) == -1)) + error("Cannot delete %s file '%s'", reason?reason:"", fullpath); + else + ret++; + } + + info("Deleting empty directory '%s'", path); + if(unlikely(rmdir(path) == -1)) + error("Cannot delete empty directory '%s'", path); + else + ret++; + + closedir(dir); + + return ret; +} diff --git a/src/common.h b/src/common.h index b82c078fa..efeebf16f 100644 --- a/src/common.h +++ b/src/common.h @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef HAVE_NETINET_IN_H #include @@ -201,12 +202,14 @@ #define NETDATA_OS_TYPE "linux" #endif /* __FreeBSD__, __APPLE__*/ +#include "statistical.h" #include "socket.h" #include "eval.h" #include "health.h" #include "rrd.h" #include "plugin_tc.h" #include "plugins_d.h" +#include "statsd.h" #include "rrd2json.h" #include "rrd2json_api_old.h" #include "web_client.h" @@ -217,6 +220,7 @@ #include "unit_test.h" #include "ipc.h" #include "backends.h" +#include "backend_prometheus.h" #include "inlined.h" #include "adaptive_resortable_list.h" #include "rrdpush.h" @@ -238,7 +242,8 @@ extern void netdata_fix_chart_name(char *s); extern void strreverse(char* begin, char* end); extern char *mystrsep(char **ptr, char *s); -extern char *trim(char *s); +extern char *trim(char *s); // remove leading and trailing spaces; may return NULL +extern char *trim_all(char *buffer); // like trim(), but also remove duplicate spaces inside the string; may return NULL 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); @@ -265,9 +270,10 @@ extern void freez(void *ptr); #endif extern void json_escape_string(char *dst, const char *src, size_t size); +extern void json_fix_string(char *s); extern void *mymmap(const char *filename, size_t size, int flags, int ksm); -extern int savememory(const char *filename, void *mem, size_t size); +extern int memory_file_save(const char *filename, void *mem, size_t size); extern int fd_is_valid(int fd); @@ -289,6 +295,8 @@ extern pid_t get_system_pid_max(void); extern unsigned int hz; extern void get_system_HZ(void); +extern int recursively_delete_dir(const char *path, const char *reason); + extern volatile sig_atomic_t netdata_exit; extern const char *os_type; diff --git a/src/daemon.c b/src/daemon.c index 42b04c401..bc02446e0 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -73,8 +73,9 @@ void create_needed_dir(const char *dir, uid_t uid, gid_t gid) error("Cannot create directory '%s'", dir); } -int become_user(const char *username, int pid_fd) -{ +int become_user(const char *username, int pid_fd) { + int am_i_root = (getuid() == 0)?1:0; + struct passwd *pw = getpwnam(username); if(!pw) { error("User %s is not present.", username); @@ -94,12 +95,12 @@ int become_user(const char *username, int pid_fd) int ngroups = (int)sysconf(_SC_NGROUPS_MAX); gid_t *supplementary_groups = NULL; - if(ngroups) { + if(ngroups > 0) { supplementary_groups = mallocz(sizeof(gid_t) * ngroups); if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) { - error("Cannot get supplementary groups of user '%s'.", username); - freez(supplementary_groups); - supplementary_groups = NULL; + if(am_i_root) + error("Cannot get supplementary groups of user '%s'.", username); + ngroups = 0; } } @@ -109,14 +110,17 @@ int become_user(const char *username, int pid_fd) chown_open_file(stdaccess_fd, uid, gid); chown_open_file(pid_fd, uid, gid); - if(supplementary_groups && ngroups) { - if(setgroups(ngroups, supplementary_groups) == -1) - error("Cannot set supplementary groups for user '%s'", username); - - freez(supplementary_groups); + if(supplementary_groups && ngroups > 0) { + if(setgroups((size_t)ngroups, supplementary_groups) == -1) { + if(am_i_root) + error("Cannot set supplementary groups for user '%s'", username); + } ngroups = 0; } + if(supplementary_groups) + freez(supplementary_groups); + #ifdef __APPLE__ if(setregid(gid, gid) != 0) { #else @@ -155,22 +159,42 @@ int become_user(const char *username, int pid_fd) return(0); } +#ifndef OOM_SCORE_ADJ_MAX +#define OOM_SCORE_ADJ_MAX 1000 +#endif +#ifndef OOM_SCORE_ADJ_MIN +#define OOM_SCORE_ADJ_MIN -1000 +#endif + static void oom_score_adj(void) { - int score = (int)config_get_number(CONFIG_SECTION_GLOBAL, "OOM score", 1000); + char buf[10 + 1]; + snprintfz(buf, 10, "%d", OOM_SCORE_ADJ_MAX); + + // check the environment + char *s = getenv("OOMScoreAdjust"); + if(!s || !*s) s = buf; + + // check netdata.conf configuration + s = config_get(CONFIG_SECTION_GLOBAL, "OOM score", s); + if(!s || !*s) s = buf; + + if(!isdigit(*s) && *s != '-' && *s != '+') { + info("Out-Of-Memory score not changed due to setting: '%s'", s); + return; + } 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); + ssize_t len = strlen(s); if(len > 0 && write(fd, buf, (size_t)len) == len) done = 1; close(fd); } if(!done) - error("Cannot adjust my Out-Of-Memory score to %d.", score); + error("Cannot adjust my Out-Of-Memory score to '%s'.", s); else - debug(D_SYSTEM, "Adjusted my Out-Of-Memory score to %d.", score); + info("Adjusted my Out-Of-Memory score to '%s'.", s); } static void process_nice_level(void) { diff --git a/src/eval.c b/src/eval.c index 122959ce4..9248109b0 100644 --- a/src/eval.c +++ b/src/eval.c @@ -723,7 +723,7 @@ static inline int parse_variable(const char **string, char *buffer, size_t len) static inline int parse_constant(const char **string, calculated_number *number) { char *end = NULL; - calculated_number n = strtold(*string, &end); + calculated_number n = str2ld(*string, &end); if(unlikely(!end || *string == end)) { *number = 0; return 0; diff --git a/src/freebsd_devstat.c b/src/freebsd_devstat.c new file mode 100644 index 000000000..5b0687d5b --- /dev/null +++ b/src/freebsd_devstat.c @@ -0,0 +1,662 @@ +#include "common.h" + +#include + +struct disk { + char *name; + uint32_t hash; + size_t len; + + // flags + int configured; + int enabled; + int updated; + + 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; + + struct disk *next; +}; + +static struct disk *disks_root = NULL, *disks_last_used = NULL; + +static size_t disks_added = 0, disks_found = 0; + +static void disk_free(struct disk *dm) { + if (likely(dm->st_io)) + rrdset_is_obsolete(dm->st_io); + if (likely(dm->st_ops)) + rrdset_is_obsolete(dm->st_ops); + if (likely(dm->st_qops)) + rrdset_is_obsolete(dm->st_qops); + if (likely(dm->st_util)) + rrdset_is_obsolete(dm->st_util); + if (likely(dm->st_iotime)) + rrdset_is_obsolete(dm->st_iotime); + if (likely(dm->st_await)) + rrdset_is_obsolete(dm->st_await); + if (likely(dm->st_avagsz)) + rrdset_is_obsolete(dm->st_avagsz); + if (likely(dm->st_svctm)) + rrdset_is_obsolete(dm->st_svctm); + + disks_added--; + freez(dm->name); + freez(dm); +} + +static void disks_cleanup() { + if (likely(disks_found == disks_added)) return; + + struct disk *dm = disks_root, *last = NULL; + while(dm) { + if (unlikely(!dm->updated)) { + // info("Removing disk '%s', linked after '%s'", dm->name, last?last->name:"ROOT"); + + if (disks_last_used == dm) + disks_last_used = last; + + struct disk *t = dm; + + if (dm == disks_root || !last) + disks_root = dm = dm->next; + + else + last->next = dm = dm->next; + + t->next = NULL; + disk_free(t); + } + else { + last = dm; + dm->updated = 0; + dm = dm->next; + } + } +} + +static struct disk *get_disk(const char *name) { + struct disk *dm; + + uint32_t hash = simple_hash(name); + + // search it, from the last position to the end + for(dm = disks_last_used ; dm ; dm = dm->next) { + if (unlikely(hash == dm->hash && !strcmp(name, dm->name))) { + disks_last_used = dm->next; + return dm; + } + } + + // search it from the beginning to the last position we used + for(dm = disks_root ; dm != disks_last_used ; dm = dm->next) { + if (unlikely(hash == dm->hash && !strcmp(name, dm->name))) { + disks_last_used = dm->next; + return dm; + } + } + + // create a new one + dm = callocz(1, sizeof(struct disk)); + dm->name = strdupz(name); + dm->hash = simple_hash(dm->name); + dm->len = strlen(dm->name); + disks_added++; + + // link it to the end + if (disks_root) { + struct disk *e; + for(e = disks_root; e->next ; e = e->next) ; + e->next = dm; + } + else + disks_root = dm; + + return dm; +} + +// -------------------------------------------------------------------------------------------------------------------- +// 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_new_disks = -1; + 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; + static SIMPLE_PATTERN *excluded_disks = NULL; + + if (unlikely(enable_new_disks == -1)) { + enable_new_disks = config_get_boolean_ondemand(CONFIG_SECTION_KERN_DEVSTAT, + "enable new disks detected at runtime", CONFIG_BOOLEAN_AUTO); + + 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); + + excluded_disks = simple_pattern_create( + config_get(CONFIG_SECTION_KERN_DEVSTAT, "disable by default disks matching", DELAULT_EXLUDED_DISKS) + , SIMPLE_PATTERN_EXACT + ); + } + + 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; + static int old_numdevs = 0; + + if (unlikely(numdevs != old_numdevs)) { + devstat_data = reallocz(devstat_data, sizeof(long) + sizeof(struct devstat) * + numdevs); // there is generation number before devstat structures + old_numdevs = numdevs; + } + 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; + + disks_found = 0; + + 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]; + 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 disk *dm = get_disk(disk); + dm->updated = 1; + disks_found++; + + if(unlikely(!dm->configured)) { + char var_name[4096 + 1]; + + // this is the first time we see this disk + + // remember we configured it + dm->configured = 1; + + dm->enabled = enable_new_disks; + + if (likely(dm->enabled)) + dm->enabled = !simple_pattern_matches(excluded_disks, disk); + + snprintfz(var_name, 4096, "%s:%s", CONFIG_SECTION_KERN_DEVSTAT, disk); + dm->enabled = config_get_boolean_ondemand(var_name, "enabled", dm->enabled); + + dm->do_io = config_get_boolean_ondemand(var_name, "bandwidth", do_io); + dm->do_ops = config_get_boolean_ondemand(var_name, "operations", do_ops); + dm->do_qops = config_get_boolean_ondemand(var_name, "queued operations", do_qops); + dm->do_util = config_get_boolean_ondemand(var_name, "utilization percentage", do_util); + dm->do_iotime = config_get_boolean_ondemand(var_name, "i/o time", do_iotime); + dm->do_await = config_get_boolean_ondemand(var_name, "average completed i/o time", + do_await); + dm->do_avagsz = config_get_boolean_ondemand(var_name, "average completed i/o bandwidth", + do_avagsz); + dm->do_svctm = config_get_boolean_ondemand(var_name, "average service time", do_svctm); + + // initialise data for differential charts + + 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 = dstat[i].duration[DEVSTAT_READ].sec * 1000 + + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; + dm->prev_dstat.duration_write_ms = dstat[i].duration[DEVSTAT_WRITE].sec * 1000 + + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE; + dm->prev_dstat.busy_time_ms = dstat[i].busy_time.sec * 1000 + + dstat[i].busy_time.frac * BINTIME_SCALE; + } + + 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; + } + + disks_cleanup(); + + return 0; +} diff --git a/src/freebsd_getifaddrs.c b/src/freebsd_getifaddrs.c new file mode 100644 index 000000000..7355fac9e --- /dev/null +++ b/src/freebsd_getifaddrs.c @@ -0,0 +1,494 @@ +#include "common.h" + +#include + +struct network_interface { + char *name; + uint32_t hash; + size_t len; + + // flags + int configured; + int enabled; + int updated; + + 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; + + struct network_interface *next; +}; + +static struct network_interface *network_interfaces_root = NULL, *network_interfaces_last_used = NULL; + +static size_t network_interfaces_added = 0, network_interfaces_found = 0; + +static void network_interface_free(struct network_interface *ifm) { + if (likely(ifm->st_bandwidth)) + rrdset_is_obsolete(ifm->st_bandwidth); + if (likely(ifm->st_packets)) + rrdset_is_obsolete(ifm->st_packets); + if (likely(ifm->st_errors)) + rrdset_is_obsolete(ifm->st_errors); + if (likely(ifm->st_drops)) + rrdset_is_obsolete(ifm->st_drops); + if (likely(ifm->st_events)) + rrdset_is_obsolete(ifm->st_events); + + network_interfaces_added--; + freez(ifm->name); + freez(ifm); +} + +static void network_interfaces_cleanup() { + if (likely(network_interfaces_found == network_interfaces_added)) return; + + struct network_interface *ifm = network_interfaces_root, *last = NULL; + while(ifm) { + if (unlikely(!ifm->updated)) { + // info("Removing network interface '%s', linked after '%s'", ifm->name, last?last->name:"ROOT"); + + if (network_interfaces_last_used == ifm) + network_interfaces_last_used = last; + + struct network_interface *t = ifm; + + if (ifm == network_interfaces_root || !last) + network_interfaces_root = ifm = ifm->next; + + else + last->next = ifm = ifm->next; + + t->next = NULL; + network_interface_free(t); + } + else { + last = ifm; + ifm->updated = 0; + ifm = ifm->next; + } + } +} + +static struct network_interface *get_network_interface(const char *name) { + struct network_interface *ifm; + + uint32_t hash = simple_hash(name); + + // search it, from the last position to the end + for(ifm = network_interfaces_last_used ; ifm ; ifm = ifm->next) { + if (unlikely(hash == ifm->hash && !strcmp(name, ifm->name))) { + network_interfaces_last_used = ifm->next; + return ifm; + } + } + + // search it from the beginning to the last position we used + for(ifm = network_interfaces_root ; ifm != network_interfaces_last_used ; ifm = ifm->next) { + if (unlikely(hash == ifm->hash && !strcmp(name, ifm->name))) { + network_interfaces_last_used = ifm->next; + return ifm; + } + } + + // create a new one + ifm = callocz(1, sizeof(struct network_interface)); + ifm->name = strdupz(name); + ifm->hash = simple_hash(ifm->name); + ifm->len = strlen(ifm->name); + network_interfaces_added++; + + // link it to the end + if (network_interfaces_root) { + struct network_interface *e; + for(e = network_interfaces_root; e->next ; e = e->next) ; + e->next = ifm; + } + else + network_interfaces_root = ifm; + + return ifm; +} + +// -------------------------------------------------------------------------------------------------------------------- +// getifaddrs + +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 enable_new_interfaces = -1; + 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; + static SIMPLE_PATTERN *excluded_interfaces = NULL; + + if (unlikely(enable_new_interfaces == -1)) { + enable_new_interfaces = config_get_boolean_ondemand(CONFIG_SECTION_GETIFADDRS, + "enable new interfaces detected at runtime", + CONFIG_BOOLEAN_AUTO); + + 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); + + excluded_interfaces = simple_pattern_create( + config_get(CONFIG_SECTION_GETIFADDRS, "disable by default interfaces matching", + DELAULT_EXLUDED_INTERFACES) + , SIMPLE_PATTERN_EXACT + ); + } + + 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 { +#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 (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); + } + + // -------------------------------------------------------------------- + + 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); + } + + // -------------------------------------------------------------------- + + network_interfaces_found = 0; + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + struct network_interface *ifm = get_network_interface(ifa->ifa_name); + ifm->updated = 1; + network_interfaces_found++; + + if (unlikely(!ifm->configured)) { + char var_name[4096 + 1]; + + // this is the first time we see this network interface + + // remember we configured it + ifm->configured = 1; + + ifm->enabled = enable_new_interfaces; + + if (likely(ifm->enabled)) + ifm->enabled = !simple_pattern_matches(excluded_interfaces, ifa->ifa_name); + + snprintfz(var_name, 4096, "%s:%s", CONFIG_SECTION_GETIFADDRS, ifa->ifa_name); + ifm->enabled = config_get_boolean_ondemand(var_name, "enabled", ifm->enabled); + + if (unlikely(ifm->enabled == CONFIG_BOOLEAN_NO)) + continue; + + ifm->do_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", do_bandwidth); + ifm->do_packets = config_get_boolean_ondemand(var_name, "packets", do_packets); + ifm->do_errors = config_get_boolean_ondemand(var_name, "errors", do_errors); + ifm->do_drops = config_get_boolean_ondemand(var_name, "drops", do_drops); + ifm->do_events = config_get_boolean_ondemand(var_name, "events", do_events); + } + + if (unlikely(!ifm->enabled)) + continue; + + // -------------------------------------------------------------------- + + 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) + #if __FreeBSD__ >= 11 + || IFA_DATA(oqdrops) +#endif + ))) { + 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; + } + + network_interfaces_cleanup(); + + return 0; +} diff --git a/src/freebsd_getmntinfo.c b/src/freebsd_getmntinfo.c new file mode 100644 index 000000000..e7ca56b57 --- /dev/null +++ b/src/freebsd_getmntinfo.c @@ -0,0 +1,293 @@ +#include "common.h" + +#include + +struct mount_point { + char *name; + uint32_t hash; + size_t len; + + // flags + int configured; + int enabled; + int updated; + + 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; + + struct mount_point *next; +}; + +static struct mount_point *mount_points_root = NULL, *mount_points_last_used = NULL; + +static size_t mount_points_added = 0, mount_points_found = 0; + +static void mount_point_free(struct mount_point *m) { + if (likely(m->st_space)) + rrdset_is_obsolete(m->st_space); + if (likely(m->st_inodes)) + rrdset_is_obsolete(m->st_inodes); + + mount_points_added--; + freez(m->name); + freez(m); +} + +static void mount_points_cleanup() { + if (likely(mount_points_found == mount_points_added)) return; + + struct mount_point *m = mount_points_root, *last = NULL; + while(m) { + if (unlikely(!m->updated)) { + // info("Removing mount point '%s', linked after '%s'", m->name, last?last->name:"ROOT"); + + if (mount_points_last_used == m) + mount_points_last_used = last; + + struct mount_point *t = m; + + if (m == mount_points_root || !last) + mount_points_root = m = m->next; + + else + last->next = m = m->next; + + t->next = NULL; + mount_point_free(t); + } + else { + last = m; + m->updated = 0; + m = m->next; + } + } +} + +static struct mount_point *get_mount_point(const char *name) { + struct mount_point *m; + + uint32_t hash = simple_hash(name); + + // search it, from the last position to the end + for(m = mount_points_last_used ; m ; m = m->next) { + if (unlikely(hash == m->hash && !strcmp(name, m->name))) { + mount_points_last_used = m->next; + return m; + } + } + + // search it from the beginning to the last position we used + for(m = mount_points_root ; m != mount_points_last_used ; m = m->next) { + if (unlikely(hash == m->hash && !strcmp(name, m->name))) { + mount_points_last_used = m->next; + return m; + } + } + + // create a new one + m = callocz(1, sizeof(struct mount_point)); + m->name = strdupz(name); + m->hash = simple_hash(m->name); + m->len = strlen(m->name); + mount_points_added++; + + // link it to the end + if (mount_points_root) { + struct mount_point *e; + for(e = mount_points_root; e->next ; e = e->next) ; + e->next = m; + } + else + mount_points_root = m; + + return m; +} + +// -------------------------------------------------------------------------------------------------------------------- +// 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 enable_new_mount_points = -1; + static int do_space = -1, do_inodes = -1; + static SIMPLE_PATTERN *excluded_mountpoints = NULL; + static SIMPLE_PATTERN *excluded_filesystems = NULL; + + if (unlikely(enable_new_mount_points == -1)) { + enable_new_mount_points = config_get_boolean_ondemand(CONFIG_SECTION_GETMNTINFO, + "enable new mount points detected at runtime", + CONFIG_BOOLEAN_AUTO); + + 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); + + 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 + ); + } + + 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.* charts"); + do_inodes = 0; + error("DISABLED: disk_inodes.* charts"); + error("DISABLED: getmntinfo module"); + return 1; + } else { + int i; + + mount_points_found = 0; + + for (i = 0; i < mntsize; i++) { + char title[4096 + 1]; + + struct mount_point *m = get_mount_point(mntbuf[i].f_mntonname); + m->updated = 1; + mount_points_found++; + + if (unlikely(!m->configured)) { + char var_name[4096 + 1]; + + // this is the first time we see this filesystem + + // remember we configured it + m->configured = 1; + + m->enabled = enable_new_mount_points; + + if (likely(m->enabled)) + m->enabled = !(simple_pattern_matches(excluded_mountpoints, mntbuf[i].f_mntonname) + || simple_pattern_matches(excluded_filesystems, mntbuf[i].f_fstypename)); + + snprintfz(var_name, 4096, "%s:%s", CONFIG_SECTION_GETMNTINFO, mntbuf[i].f_mntonname); + m->enabled = config_get_boolean_ondemand(var_name, "enabled", m->enabled); + + if (unlikely(m->enabled == CONFIG_BOOLEAN_NO)) + continue; + + m->do_space = config_get_boolean_ondemand(var_name, "space usage", do_space); + m->do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", do_inodes); + } + + if (unlikely(!m->enabled)) + continue; + + if (unlikely(mntbuf[i].f_flags & MNT_RDONLY && !m->collected)) + continue; + + // -------------------------------------------------------------------------- + + 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(m->st_space); + + 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 (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(m->st_inodes); + + 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; + } + + mount_points_cleanup(); + + return 0; +} diff --git a/src/freebsd_ipfw.c b/src/freebsd_ipfw.c new file mode 100644 index 000000000..b89650a04 --- /dev/null +++ b/src/freebsd_ipfw.c @@ -0,0 +1,360 @@ +#include "common.h" + +#include + +#define FREE_MEM_THRESHOLD 10000 // number of unused chunks that trigger memory freeing + +#define COMMON_IPFW_ERROR() error("DISABLED: ipfw.packets chart"); \ + error("DISABLED: ipfw.bytes chart"); \ + error("DISABLED: ipfw.dyn_active chart"); \ + error("DISABLED: ipfw.dyn_expired chart"); \ + error("DISABLED: ipfw.mem chart"); + +// -------------------------------------------------------------------------------------------------------------------- +// ipfw + +int do_ipfw(int update_every, usec_t dt) { + (void)dt; +#if __FreeBSD__ >= 11 + + static int do_static = -1, do_dynamic = -1, do_mem = -1; + + if (unlikely(do_static == -1)) { + do_static = config_get_boolean("plugin:freebsd:ipfw", "counters for static rules", 1); + do_dynamic = config_get_boolean("plugin:freebsd:ipfw", "number of dynamic rules", 1); + do_mem = config_get_boolean("plugin:freebsd:ipfw", "allocated memory", 1); + } + + // variables for getting ipfw configuration + + int error; + static int ipfw_socket = -1; + static ipfw_cfg_lheader *cfg = NULL; + ip_fw3_opheader *op3 = NULL; + static socklen_t *optlen = NULL, cfg_size = 0; + + // variables for static rules handling + + ipfw_obj_ctlv *ctlv = NULL; + ipfw_obj_tlv *rbase = NULL; + int rcnt = 0; + + int n, seen; + struct ip_fw_rule *rule; + struct ip_fw_bcounter *cntr; + int c = 0; + + char rule_num_str[12]; + + // variables for dynamic rules handling + + caddr_t dynbase = NULL; + size_t dynsz = 0; + size_t readsz = sizeof(*cfg);; + int ttype = 0; + ipfw_obj_tlv *tlv; + ipfw_dyn_rule *dyn_rule; + uint16_t rulenum, prev_rulenum = IPFW_DEFAULT_RULE; + unsigned srn, static_rules_num = 0; + static size_t dyn_rules_num_size = 0; + + static struct dyn_rule_num { + uint16_t rule_num; + uint32_t active_rules; + uint32_t expired_rules; + } *dyn_rules_num = NULL; + + uint32_t *dyn_rules_counter; + + if (likely(do_static | do_dynamic | do_mem)) { + + // initialize the smallest ipfw_cfg_lheader possible + + if (unlikely((optlen == NULL) || (cfg == NULL))) { + optlen = reallocz(optlen, sizeof(socklen_t)); + *optlen = cfg_size = 32; + cfg = reallocz(cfg, *optlen); + } + + // get socket descriptor and initialize ipfw_cfg_lheader structure + + if (unlikely(ipfw_socket == -1)) + ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (unlikely(ipfw_socket == -1)) { + error("FREEBSD: can't get socket for ipfw configuration"); + error("FREEBSD: run netdata as root to get access to ipfw data"); + COMMON_IPFW_ERROR(); + return 1; + } + + bzero(cfg, 32); + cfg->flags = IPFW_CFG_GET_STATIC | IPFW_CFG_GET_COUNTERS | IPFW_CFG_GET_STATES; + op3 = &cfg->opheader; + op3->opcode = IP_FW_XGET; + + // get ifpw configuration size than get configuration + + *optlen = cfg_size; + error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen); + if (error) + if (errno != ENOMEM) { + error("FREEBSD: ipfw socket reading error"); + COMMON_IPFW_ERROR(); + return 1; + } + if ((cfg->size > cfg_size) || ((cfg_size - cfg->size) > sizeof(struct dyn_rule_num) * FREE_MEM_THRESHOLD)) { + *optlen = cfg_size = cfg->size; + cfg = reallocz(cfg, *optlen); + bzero(cfg, 32); + cfg->flags = IPFW_CFG_GET_STATIC | IPFW_CFG_GET_COUNTERS | IPFW_CFG_GET_STATES; + op3 = &cfg->opheader; + op3->opcode = IP_FW_XGET; + error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen); + if (error) { + error("FREEBSD: ipfw socket reading error"); + COMMON_IPFW_ERROR(); + return 1; + } + } + + // go through static rules configuration structures + + ctlv = (ipfw_obj_ctlv *) (cfg + 1); + + if (cfg->flags & IPFW_CFG_GET_STATIC) { + /* We've requested static rules */ + if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { + readsz += ctlv->head.length; + ctlv = (ipfw_obj_ctlv *) ((caddr_t) ctlv + + ctlv->head.length); + } + + if (ctlv->head.type == IPFW_TLV_RULE_LIST) { + rbase = (ipfw_obj_tlv *) (ctlv + 1); + rcnt = ctlv->count; + readsz += ctlv->head.length; + ctlv = (ipfw_obj_ctlv *) ((caddr_t) ctlv + ctlv->head.length); + } + } + + if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != *optlen)) { + /* We may have some dynamic states */ + dynsz = *optlen - readsz; + /* Skip empty header */ + if (dynsz != sizeof(ipfw_obj_ctlv)) + dynbase = (caddr_t) ctlv; + else + dynsz = 0; + } + + // -------------------------------------------------------------------- + + if (likely(do_mem)) { + static RRDSET *st_mem = NULL; + static RRDDIM *rd_dyn_mem = NULL; + static RRDDIM *rd_stat_mem = NULL; + + if (unlikely(!st_mem)) { + st_mem = rrdset_create_localhost("ipfw", + "mem", + NULL, + "memory allocated", + NULL, + "Memory allocated by rules", + "bytes", + 3005, + update_every, + RRDSET_TYPE_STACKED + ); + rrdset_flag_set(st_mem, RRDSET_FLAG_DETAIL); + + rd_dyn_mem = rrddim_add(st_mem, "dynamic", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_stat_mem = rrddim_add(st_mem, "static", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } else + rrdset_next(st_mem); + + rrddim_set_by_pointer(st_mem, rd_dyn_mem, dynsz); + rrddim_set_by_pointer(st_mem, rd_stat_mem, *optlen - dynsz); + rrdset_done(st_mem); + } + + // -------------------------------------------------------------------- + + static RRDSET *st_packets = NULL, *st_bytes = NULL; + RRDDIM *rd_packets = NULL, *rd_bytes = NULL; + + if (likely(do_static || do_dynamic)) { + if (likely(do_static)) { + if (unlikely(!st_packets)) + st_packets = rrdset_create_localhost("ipfw", + "packets", + NULL, + "static rules", + NULL, + "Packets", + "packets/s", + 3001, + update_every, + RRDSET_TYPE_STACKED + ); + else + rrdset_next(st_packets); + + if (unlikely(!st_bytes)) + st_bytes = rrdset_create_localhost("ipfw", + "bytes", + NULL, + "static rules", + NULL, + "Bytes", + "bytes/s", + 3002, + update_every, + RRDSET_TYPE_STACKED + ); + else + rrdset_next(st_bytes); + } + + for (n = seen = 0; n < rcnt; n++, rbase = (ipfw_obj_tlv *) ((caddr_t) rbase + rbase->length)) { + cntr = (struct ip_fw_bcounter *) (rbase + 1); + rule = (struct ip_fw_rule *) ((caddr_t) cntr + cntr->size); + if (rule->rulenum != prev_rulenum) + static_rules_num++; + if (rule->rulenum > IPFW_DEFAULT_RULE) + break; + + if (likely(do_static)) { + sprintf(rule_num_str, "%d_%d", rule->rulenum, rule->id); + + rd_packets = rrddim_find(st_packets, rule_num_str); + if (unlikely(!rd_packets)) + rd_packets = rrddim_add(st_packets, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_set_by_pointer(st_packets, rd_packets, cntr->pcnt); + + rd_bytes = rrddim_find(st_bytes, rule_num_str); + if (unlikely(!rd_bytes)) + rd_bytes = rrddim_add(st_bytes, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rrddim_set_by_pointer(st_bytes, rd_bytes, cntr->bcnt); + } + + c += rbase->length; + seen++; + } + + if (likely(do_static)) { + rrdset_done(st_packets); + rrdset_done(st_bytes); + } + } + + // -------------------------------------------------------------------- + + // go through dynamic rules configuration structures + + if (likely(do_dynamic && (dynsz > 0))) { + if ((dyn_rules_num_size < sizeof(struct dyn_rule_num) * static_rules_num) || + ((dyn_rules_num_size - sizeof(struct dyn_rule_num) * static_rules_num) > + sizeof(struct dyn_rule_num) * FREE_MEM_THRESHOLD)) { + dyn_rules_num_size = sizeof(struct dyn_rule_num) * static_rules_num; + dyn_rules_num = reallocz(dyn_rules_num, dyn_rules_num_size); + } + bzero(dyn_rules_num, sizeof(struct dyn_rule_num) * static_rules_num); + dyn_rules_num->rule_num = IPFW_DEFAULT_RULE; + + if (dynsz > 0 && ctlv->head.type == IPFW_TLV_DYNSTATE_LIST) { + dynbase += sizeof(*ctlv); + dynsz -= sizeof(*ctlv); + ttype = IPFW_TLV_DYN_ENT; + } + + while (dynsz > 0) { + tlv = (ipfw_obj_tlv *) dynbase; + if (tlv->type != ttype) + break; + + dyn_rule = (ipfw_dyn_rule *) (tlv + 1); + bcopy(&dyn_rule->rule, &rulenum, sizeof(rulenum)); + + for (srn = 0; srn < (static_rules_num - 1); srn++) { + if (dyn_rule->expire > 0) + dyn_rules_counter = &dyn_rules_num[srn].active_rules; + else + dyn_rules_counter = &dyn_rules_num[srn].expired_rules; + if (dyn_rules_num[srn].rule_num == rulenum) { + (*dyn_rules_counter)++; + break; + } + if (dyn_rules_num[srn].rule_num == IPFW_DEFAULT_RULE) { + dyn_rules_num[srn].rule_num = rulenum; + dyn_rules_num[srn + 1].rule_num = IPFW_DEFAULT_RULE; + (*dyn_rules_counter)++; + break; + } + } + + dynsz -= tlv->length; + dynbase += tlv->length; + } + + // -------------------------------------------------------------------- + + static RRDSET *st_active = NULL, *st_expired = NULL; + RRDDIM *rd_active = NULL, *rd_expired = NULL; + + if (unlikely(!st_active)) + st_active = rrdset_create_localhost("ipfw", + "active", + NULL, + "dynamic_rules", + NULL, + "Active rules", + "rules", + 3003, + update_every, + RRDSET_TYPE_STACKED + ); + else + rrdset_next(st_active); + + if (unlikely(!st_expired)) + st_expired = rrdset_create_localhost("ipfw", + "expired", + NULL, + "dynamic_rules", + NULL, + "Expired rules", + "rules", + 3004, + update_every, + RRDSET_TYPE_STACKED + ); + else + rrdset_next(st_expired); + + for (srn = 0; (srn < (static_rules_num - 1)) && (dyn_rules_num[srn].rule_num != IPFW_DEFAULT_RULE); srn++) { + sprintf(rule_num_str, "%d", dyn_rules_num[srn].rule_num); + + rd_active = rrddim_find(st_active, rule_num_str); + if (unlikely(!rd_active)) + rd_active = rrddim_add(st_active, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_set_by_pointer(st_active, rd_active, dyn_rules_num[srn].active_rules); + + rd_expired = rrddim_find(st_expired, rule_num_str); + if (unlikely(!rd_expired)) + rd_expired = rrddim_add(st_expired, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rrddim_set_by_pointer(st_expired, rd_expired, dyn_rules_num[srn].expired_rules); + } + + rrdset_done(st_active); + rrdset_done(st_expired); + } + } + + return 0; +#else + error("FREEBSD: ipfw charts supported for FreeBSD 11.0 and newer releases only"); + COMMON_IPFW_ERROR(); + return 1; +#endif +} diff --git a/src/freebsd_kstat_zfs.c b/src/freebsd_kstat_zfs.c new file mode 100644 index 000000000..17642994e --- /dev/null +++ b/src/freebsd_kstat_zfs.c @@ -0,0 +1,212 @@ +#include "common.h" +#include "zfs_common.h" + +struct arcstats arcstats = { 0 }; + +// kstat.zfs.misc.arcstats +int do_kstat_zfs_misc_arcstats(int update_every, usec_t dt) { + (void)dt; + + unsigned long long l2_size; + size_t uint64_t_size = sizeof(uint64_t); + static struct mibs { + int hits[5]; + int misses[5]; + int demand_data_hits[5]; + int demand_data_misses[5]; + int demand_metadata_hits[5]; + int demand_metadata_misses[5]; + int prefetch_data_hits[5]; + int prefetch_data_misses[5]; + int prefetch_metadata_hits[5]; + int prefetch_metadata_misses[5]; + int mru_hits[5]; + int mru_ghost_hits[5]; + int mfu_hits[5]; + int mfu_ghost_hits[5]; + int deleted[5]; + int mutex_miss[5]; + int evict_skip[5]; + int evict_not_enough[5]; + int evict_l2_cached[5]; + int evict_l2_eligible[5]; + int evict_l2_ineligible[5]; + int evict_l2_skip[5]; + int hash_elements[5]; + int hash_elements_max[5]; + int hash_collisions[5]; + int hash_chains[5]; + int hash_chain_max[5]; + int p[5]; + int c[5]; + int c_min[5]; + int c_max[5]; + int size[5]; + int hdr_size[5]; + int data_size[5]; + int metadata_size[5]; + int other_size[5]; + int anon_size[5]; + int anon_evictable_data[5]; + int anon_evictable_metadata[5]; + int mru_size[5]; + int mru_evictable_data[5]; + int mru_evictable_metadata[5]; + int mru_ghost_size[5]; + int mru_ghost_evictable_data[5]; + int mru_ghost_evictable_metadata[5]; + int mfu_size[5]; + int mfu_evictable_data[5]; + int mfu_evictable_metadata[5]; + int mfu_ghost_size[5]; + int mfu_ghost_evictable_data[5]; + int mfu_ghost_evictable_metadata[5]; + int l2_hits[5]; + int l2_misses[5]; + int l2_feeds[5]; + int l2_rw_clash[5]; + int l2_read_bytes[5]; + int l2_write_bytes[5]; + int l2_writes_sent[5]; + int l2_writes_done[5]; + int l2_writes_error[5]; + int l2_writes_lock_retry[5]; + int l2_evict_lock_retry[5]; + int l2_evict_reading[5]; + int l2_evict_l1cached[5]; + int l2_free_on_write[5]; + int l2_cdata_free_on_write[5]; + int l2_abort_lowmem[5]; + int l2_cksum_bad[5]; + int l2_io_error[5]; + int l2_size[5]; + int l2_asize[5]; + int l2_hdr_size[5]; + int l2_compress_successes[5]; + int l2_compress_zeros[5]; + int l2_compress_failures[5]; + int memory_throttle_count[5]; + int duplicate_buffers[5]; + int duplicate_buffers_size[5]; + int duplicate_reads[5]; + int memory_direct_count[5]; + int memory_indirect_count[5]; + int arc_no_grow[5]; + int arc_tempreserve[5]; + int arc_loaned_bytes[5]; + int arc_prune[5]; + int arc_meta_used[5]; + int arc_meta_limit[5]; + int arc_meta_max[5]; + int arc_meta_min[5]; + int arc_need_free[5]; + int arc_sys_free[5]; + } mibs; + + l2exist = -1; + + if(unlikely(sysctlbyname("kstat.zfs.misc.arcstats.l2_size", &l2_size, &uint64_t_size, NULL, 0))) + return 0; + + if(likely(l2_size)) + l2exist = 1; + else + l2exist = 0; + + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.hits", mibs.hits, arcstats.hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.misses", mibs.misses, arcstats.misses); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.demand_data_hits", mibs.demand_data_hits, arcstats.demand_data_hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.demand_data_misses", mibs.demand_data_misses, arcstats.demand_data_misses); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.demand_metadata_hits", mibs.demand_metadata_hits, arcstats.demand_metadata_hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.demand_metadata_misses", mibs.demand_metadata_misses, arcstats.demand_metadata_misses); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.prefetch_data_hits", mibs.prefetch_data_hits, arcstats.prefetch_data_hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.prefetch_data_misses", mibs.prefetch_data_misses, arcstats.prefetch_data_misses); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.prefetch_metadata_hits", mibs.prefetch_metadata_hits, arcstats.prefetch_metadata_hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.prefetch_metadata_misses", mibs.prefetch_metadata_misses, arcstats.prefetch_metadata_misses); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mru_hits", mibs.mru_hits, arcstats.mru_hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mru_ghost_hits", mibs.mru_ghost_hits, arcstats.mru_ghost_hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mfu_hits", mibs.mfu_hits, arcstats.mfu_hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mfu_ghost_hits", mibs.mfu_ghost_hits, arcstats.mfu_ghost_hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.deleted", mibs.deleted, arcstats.deleted); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mutex_miss", mibs.mutex_miss, arcstats.mutex_miss); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.evict_skip", mibs.evict_skip, arcstats.evict_skip); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.evict_not_enough", mibs.evict_not_enough, arcstats.evict_not_enough); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.evict_l2_cached", mibs.evict_l2_cached, arcstats.evict_l2_cached); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.evict_l2_eligible", mibs.evict_l2_eligible, arcstats.evict_l2_eligible); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.evict_l2_ineligible", mibs.evict_l2_ineligible, arcstats.evict_l2_ineligible); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.evict_l2_skip", mibs.evict_l2_skip, arcstats.evict_l2_skip); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.hash_elements", mibs.hash_elements, arcstats.hash_elements); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.hash_elements_max", mibs.hash_elements_max, arcstats.hash_elements_max); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.hash_collisions", mibs.hash_collisions, arcstats.hash_collisions); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.hash_chains", mibs.hash_chains, arcstats.hash_chains); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.hash_chain_max", mibs.hash_chain_max, arcstats.hash_chain_max); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.p", mibs.p, arcstats.p); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.c", mibs.c, arcstats.c); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.c_min", mibs.c_min, arcstats.c_min); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.c_max", mibs.c_max, arcstats.c_max); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.size", mibs.size, arcstats.size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.hdr_size", mibs.hdr_size, arcstats.hdr_size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.data_size", mibs.data_size, arcstats.data_size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.metadata_size", mibs.metadata_size, arcstats.metadata_size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.other_size", mibs.other_size, arcstats.other_size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.anon_size", mibs.anon_size, arcstats.anon_size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.anon_evictable_data", mibs.anon_evictable_data, arcstats.anon_evictable_data); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.anon_evictable_metadata", mibs.anon_evictable_metadata, arcstats.anon_evictable_metadata); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mru_size", mibs.mru_size, arcstats.mru_size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mru_evictable_data", mibs.mru_evictable_data, arcstats.mru_evictable_data); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mru_evictable_metadata", mibs.mru_evictable_metadata, arcstats.mru_evictable_metadata); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mru_ghost_size", mibs.mru_ghost_size, arcstats.mru_ghost_size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mru_ghost_evictable_data", mibs.mru_ghost_evictable_data, arcstats.mru_ghost_evictable_data); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mru_ghost_evictable_metadata", mibs.mru_ghost_evictable_metadata, arcstats.mru_ghost_evictable_metadata); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mfu_size", mibs.mfu_size, arcstats.mfu_size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mfu_evictable_data", mibs.mfu_evictable_data, arcstats.mfu_evictable_data); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mfu_evictable_metadata", mibs.mfu_evictable_metadata, arcstats.mfu_evictable_metadata); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mfu_ghost_size", mibs.mfu_ghost_size, arcstats.mfu_ghost_size); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mfu_ghost_evictable_data", mibs.mfu_ghost_evictable_data, arcstats.mfu_ghost_evictable_data); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.mfu_ghost_evictable_metadata", mibs.mfu_ghost_evictable_metadata, arcstats.mfu_ghost_evictable_metadata); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_hits", mibs.l2_hits, arcstats.l2_hits); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_misses", mibs.l2_misses, arcstats.l2_misses); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_feeds", mibs.l2_feeds, arcstats.l2_feeds); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_rw_clash", mibs.l2_rw_clash, arcstats.l2_rw_clash); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_read_bytes", mibs.l2_read_bytes, arcstats.l2_read_bytes); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_write_bytes", mibs.l2_write_bytes, arcstats.l2_write_bytes); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_writes_sent", mibs.l2_writes_sent, arcstats.l2_writes_sent); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_writes_done", mibs.l2_writes_done, arcstats.l2_writes_done); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_writes_error", mibs.l2_writes_error, arcstats.l2_writes_error); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_writes_lock_retry", mibs.l2_writes_lock_retry, arcstats.l2_writes_lock_retry); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_evict_lock_retry", mibs.l2_evict_lock_retry, arcstats.l2_evict_lock_retry); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_evict_reading", mibs.l2_evict_reading, arcstats.l2_evict_reading); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_evict_l1cached", mibs.l2_evict_l1cached, arcstats.l2_evict_l1cached); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_free_on_write", mibs.l2_free_on_write, arcstats.l2_free_on_write); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_cdata_free_on_write", mibs.l2_cdata_free_on_write, arcstats.l2_cdata_free_on_write); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_abort_lowmem", mibs.l2_abort_lowmem, arcstats.l2_abort_lowmem); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_cksum_bad", mibs.l2_cksum_bad, arcstats.l2_cksum_bad); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_io_error", mibs.l2_io_error, arcstats.l2_io_error); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_size", mibs.l2_size, arcstats.l2_size); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_asize", mibs.l2_asize, arcstats.l2_asize); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_hdr_size", mibs.l2_hdr_size, arcstats.l2_hdr_size); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_compress_successes", mibs.l2_compress_successes, arcstats.l2_compress_successes); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_compress_zeros", mibs.l2_compress_zeros, arcstats.l2_compress_zeros); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.l2_compress_failures", mibs.l2_compress_failures, arcstats.l2_compress_failures); + GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.memory_throttle_count", mibs.memory_throttle_count, arcstats.memory_throttle_count); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.duplicate_buffers", mibs.duplicate_buffers, arcstats.duplicate_buffers); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.duplicate_buffers_size", mibs.duplicate_buffers_size, arcstats.duplicate_buffers_size); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.duplicate_reads", mibs.duplicate_reads, arcstats.duplicate_reads); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.memory_direct_count", mibs.memory_direct_count, arcstats.memory_direct_count); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.memory_indirect_count", mibs.memory_indirect_count, arcstats.memory_indirect_count); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_no_grow", mibs.arc_no_grow, arcstats.arc_no_grow); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_tempreserve", mibs.arc_tempreserve, arcstats.arc_tempreserve); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_loaned_bytes", mibs.arc_loaned_bytes, arcstats.arc_loaned_bytes); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_prune", mibs.arc_prune, arcstats.arc_prune); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_meta_used", mibs.arc_meta_used, arcstats.arc_meta_used); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_meta_limit", mibs.arc_meta_limit, arcstats.arc_meta_limit); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_meta_max", mibs.arc_meta_max, arcstats.arc_meta_max); + // not used: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_meta_min", mibs.arc_meta_min, arcstats.arc_meta_min); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_need_free", mibs.arc_need_free, arcstats.arc_need_free); + // missing mib: GETSYSCTL_SIMPLE("kstat.zfs.misc.arcstats.arc_sys_free", mibs.arc_sys_free, arcstats.arc_sys_free); + + generate_charts_arcstats(update_every); + generate_charts_arc_summary(update_every); + + return 0; +} \ No newline at end of file diff --git a/src/freebsd_sysctl.c b/src/freebsd_sysctl.c index 965c1cbbf..d2f0eaa82 100644 --- a/src/freebsd_sysctl.c +++ b/src/freebsd_sysctl.c @@ -1,8 +1,6 @@ #include "common.h" #include -#include -#include #include #define _KERNEL @@ -12,8 +10,6 @@ #undef _KERNEL #include -#include -#include #include #include @@ -29,12 +25,6 @@ // -------------------------------------------------------------------------------------------------------------------- // common definitions and variables -#define KILO_FACTOR 1024 -#define MEGA_FACTOR 1048576 // 1024 * 1024 -#define GIGA_FACTOR 1073741824 // 1024 * 1024 * 1024 - -#define MAX_INT_DIGITS 10 // maximum number of digits for int - int system_pagesize = PAGE_SIZE; int number_of_cpus = 1; @@ -311,8 +301,10 @@ int do_kern_cp_times(int update_every, usec_t dt) { static int mib[2] = {0, 0}; long cp_time[CPUSTATES]; static long *pcpu_cp_time = NULL; + static int old_number_of_cpus = 0; - pcpu_cp_time = reallocz(pcpu_cp_time, sizeof(cp_time) * number_of_cpus); + if(unlikely(number_of_cpus != old_number_of_cpus)) + 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"); @@ -331,12 +323,10 @@ int do_kern_cp_times(int update_every, usec_t dt) { 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; } for (i = 0; i < number_of_cpus; i++) { @@ -375,6 +365,8 @@ int do_kern_cp_times(int update_every, usec_t dt) { rrdset_done(all_cpu_charts[i].st); } } + + old_number_of_cpus = number_of_cpus; } return 0; @@ -386,7 +378,7 @@ int do_kern_cp_times(int update_every, usec_t dt) { 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); + size_t intrcnt_size = 0; unsigned long i; if (unlikely(GETSYSCTL_SIZE("hw.intrcnt", mib_hw_intrcnt, intrcnt_size))) { @@ -396,11 +388,13 @@ int do_hw_intcnt(int update_every, usec_t dt) { return 1; } else { unsigned long nintr = 0; + static unsigned long old_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(nintr != old_nintr)) + 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"); @@ -443,7 +437,8 @@ int do_hw_intcnt(int update_every, usec_t dt) { static char *intrnames = NULL; size = nintr * (MAXCOMLEN + 1); - intrnames = reallocz(intrnames, size); + if (unlikely(nintr != old_nintr)) + 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"); @@ -484,6 +479,8 @@ int do_hw_intcnt(int update_every, usec_t dt) { rrdset_done(st_interrupts); } } + + old_nintr = nintr; } return 0; @@ -931,8 +928,12 @@ int do_kern_ipc_sem(int update_every, usec_t dt) { return 1; } else { static struct semid_kernel *ipc_sem_data = NULL; + static int old_semmni = 0; - ipc_sem_data = reallocz(ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni); + if (unlikely(ipc_sem.semmni != old_semmni)) { + ipc_sem_data = reallocz(ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni); + old_semmni = 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"); @@ -1019,8 +1020,12 @@ int do_kern_ipc_shm(int update_every, usec_t dt) { return 1; } else { static struct shmid_kernel *ipc_shm_data = NULL; + static u_long old_shmmni = 0; - ipc_shm_data = reallocz(ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni); + if (unlikely(ipc_shm.shmmni != old_shmmni)) { + ipc_shm_data = reallocz(ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni); + old_shmmni = 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"); @@ -1111,8 +1116,12 @@ int do_kern_ipc_msq(int update_every, usec_t dt) { return 1; } else { static struct msqid_kernel *ipc_msq_data = NULL; + static int old_msgmni = 0; - ipc_msq_data = reallocz(ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni); + if (unlikely(ipc_msq.msgmni != old_msgmni)) { + ipc_msq_data = reallocz(ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni); + old_msgmni = 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"); @@ -1259,7 +1268,7 @@ int do_net_isr(int update_every, usec_t dt) { 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); + size_t netisr_workstream_size = 0, netisr_work_size = 0; 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; @@ -1276,14 +1285,25 @@ int do_net_isr(int update_every, usec_t dt) { } else if (unlikely(GETSYSCTL_SIZE("net.isr.work", mib_work, netisr_work_size))) { common_error = 1; } else { + static size_t old_netisr_workstream_size = 0; + 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(netisr_workstream_size != old_netisr_workstream_size)) { + netisr_workstream = reallocz(netisr_workstream, + num_netisr_workstreams * sizeof(struct sysctl_netisr_workstream)); + old_netisr_workstream_size = netisr_workstream_size; + } if (unlikely(GETSYSCTL_WSIZE("net.isr.workstream", mib_workstream, netisr_workstream, num_netisr_workstreams * sizeof(struct sysctl_netisr_workstream)))){ common_error = 1; } else { + static size_t old_netisr_work_size = 0; + 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(netisr_work_size != old_netisr_work_size)) { + netisr_work = reallocz(netisr_work, num_netisr_works * sizeof(struct sysctl_netisr_work)); + old_netisr_work_size = netisr_work_size; + } if (unlikely(GETSYSCTL_WSIZE("net.isr.work", mib_work, netisr_work, num_netisr_works * sizeof(struct sysctl_netisr_work)))){ common_error = 1; @@ -1301,8 +1321,12 @@ int do_net_isr(int update_every, usec_t dt) { } else { unsigned long i, n; int j; + static int old_number_of_cpus = 0; - netisr_stats = reallocz(netisr_stats, (number_of_cpus + 1) * sizeof(struct netisr_stats)); + if (unlikely(number_of_cpus != old_number_of_cpus)) { + netisr_stats = reallocz(netisr_stats, (number_of_cpus + 1) * sizeof(struct netisr_stats)); + old_number_of_cpus = number_of_cpus; + } 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++) { @@ -2749,1216 +2773,3 @@ int do_net_inet6_icmp6_stats(int update_every, usec_t dt) { 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.* charts"); - do_inodes = 0; - 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++) { - - 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; - - // -------------------------------------------------------------------------- - - 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(m->st_space); - - 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 (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(m->st_inodes); - - 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 - -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 { - #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 (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); - } - - // -------------------------------------------------------------------- - - 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 index 4459de7ca..146268a53 100644 --- a/src/freeipmi_plugin.c +++ b/src/freeipmi_plugin.c @@ -242,7 +242,7 @@ _get_sensor_type_string (int sensor_type) static int debug = 0; -static int netdata_update_every = 5; +static int netdata_update_every = 5; // this is the minimum update frequency static int netdata_priority = 90000; static int netdata_do_sel = 1; @@ -1403,7 +1403,7 @@ int ipmi_detect_speed_secs(struct ipmi_monitoring_ipmi_config *ipmi_config) { // we find the average in microseconds // and we round-up to the closest second - return (( total * 2 / checks / 1000000 ) + 1); + return (int)(( total * 2 / checks / 1000000 ) + 1); } int main (int argc, char **argv) { @@ -1426,15 +1426,14 @@ int main (int argc, char **argv) { int i, freq = 0; for(i = 1; i < argc ; i++) { - if(!freq) { + if(isdigit(*argv[i]) && !freq) { int n = atoi(argv[i]); - if(n > 0) { + if(n > 0 && freq < 86400) { freq = n; continue; } } - - if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) { + else if(strcmp("version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) { printf("freeipmi.plugin %s\n", VERSION); exit(0); } @@ -1568,7 +1567,7 @@ int main (int argc, char **argv) { 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) { + if(freq > netdata_update_every) { info("enforcing minimum data collection frequency, calculated to %d seconds.", freq); netdata_update_every = freq; } diff --git a/src/health.c b/src/health.c index 46b27db6f..cc470f81f 100644 --- a/src/health.c +++ b/src/health.c @@ -148,7 +148,7 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { 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, + host->registry_hostname, ae->unique_id, ae->alarm_id, ae->alarm_event_id, @@ -356,9 +356,16 @@ void *health_main(void *ptr) { // 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))) + if(unlikely(now - last_now > 2 * (now_boottime - last_now_boottime))) { apply_hibernation_delay = 1; + info("Postponing alarm checks for %ld seconds, due to boottime discrepancy (realtime dt: %ld, boottime dt: %ld)." + , hibernation_delay + , (long)(now - last_now) + , (long)(now_boottime - last_now_boottime) + ); + } + last_now = now; last_now_boottime = now_boottime; @@ -374,11 +381,9 @@ void *health_main(void *ptr) { if(unlikely(apply_hibernation_delay)) { - info("Postponing alarm checks for %ld seconds, on host '%s', due to boottime discrepancy (realtime dt: %ld, boottime dt: %ld)." + info("Postponing alarm checks for %ld seconds, on host '%s'." , hibernation_delay , host->hostname - , (long)(now - last_now) - , (long)(now_boottime - last_now_boottime) ); host->health_delay_up_to = now + hibernation_delay; diff --git a/src/health_config.c b/src/health_config.c index ad954cbe1..b4655dc78 100644 --- a/src/health_config.c +++ b/src/health_config.c @@ -32,8 +32,8 @@ static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) { 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); + if(!RRDCALC_HAS_DB_LOOKUP(rc) && !rc->calculation && !rc->warning && !rc->critical) { + error("Health configuration for alarm '%s.%s' is useless (no db lookup, no calculation, no warning and no critical expressions)", rc->chart?rc->chart:"NOCHART", rc->name); return 0; } @@ -136,7 +136,7 @@ static inline int health_parse_duration(char *string, int *result) { } char *e = NULL; - calculated_number n = strtold(string, &e); + calculated_number n = str2ld(string, &e); if(e && *e) { switch (*e) { case 'Y': @@ -241,10 +241,10 @@ static inline int health_parse_delay( if(!given_max) { if((*delay_max_duration) < (*delay_up_duration) * (*delay_multiplier)) - *delay_max_duration = (*delay_up_duration) * (*delay_multiplier); + *delay_max_duration = (int)((*delay_up_duration) * (*delay_multiplier)); if((*delay_max_duration) < (*delay_down_duration) * (*delay_multiplier)) - *delay_max_duration = (*delay_down_duration) * (*delay_multiplier); + *delay_max_duration = (int)((*delay_down_duration) * (*delay_multiplier)); } return 1; @@ -381,37 +381,6 @@ static inline int health_parse_db_lookup( 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); @@ -485,7 +454,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) { int stop_appending = !s; line++; s = trim(buffer); - if(!s) continue; + if(!s || *s == '#') continue; append = strlen(s); if(!stop_appending && s[append - 1] == '\\') { @@ -509,8 +478,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) { s++; char *value = s; - key = trim_all_spaces(key); - value = trim_all_spaces(value); + key = trim_all(key); + value = trim_all(value); if(!key) { error("Health configuration has invalid line %zu of file '%s/%s'. Keyword is empty. Ignoring it.", line, path, filename); @@ -593,7 +562,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) { } else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) { char *e; - rc->green = strtold(value, &e); + rc->green = str2ld(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); @@ -601,7 +570,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) { } else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) { char *e; - rc->red = strtold(value, &e); + rc->red = str2ld(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); @@ -717,7 +686,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) { } else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) { char *e; - rt->green = strtold(value, &e); + rt->green = str2ld(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); @@ -725,7 +694,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) { } else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) { char *e; - rt->red = strtold(value, &e); + rt->red = str2ld(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); diff --git a/src/inlined.h b/src/inlined.h index 0dc11c950..f1812ba1c 100644 --- a/src/inlined.h +++ b/src/inlined.h @@ -123,6 +123,103 @@ static inline unsigned long long str2ull(const char *s) { return n; } +static inline long long str2ll(const char *s, char **endptr) { + int negative = 0; + + if(unlikely(*s == '-')) { + s++; + negative = 1; + } + else if(unlikely(*s == '+')) + s++; + + long long n = 0; + char c; + for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + n *= 10; + n += c - '0'; + } + + if(unlikely(endptr)) + *endptr = (char *)s; + + if(unlikely(negative)) + return -n; + else + return n; +} + +static inline long double str2ld(const char *s, char **endptr) { + int negative = 0; + const char *start = s; + unsigned long long integer_part = 0; + unsigned long decimal_part = 0; + size_t decimal_digits = 0; + + switch(*s) { + case '-': + s++; + negative = 1; + break; + + case '+': + s++; + break; + + case 'n': + if(s[1] == 'a' && s[2] == 'n') { + if(endptr) *endptr = (char *)&s[3]; + return NAN; + } + break; + + case 'i': + if(s[1] == 'n' && s[2] == 'f') { + if(endptr) *endptr = (char *)&s[3]; + return INFINITY; + } + break; + + default: + break; + } + + while (*s >= '0' && *s <= '9') { + integer_part = (integer_part * 10) + (*s - '0'); + s++; + } + + if(unlikely(*s == '.')) { + decimal_part = 0; + s++; + + while (*s >= '0' && *s <= '9') { + decimal_part = (decimal_part * 10) + (*s - '0'); + s++; + decimal_digits++; + } + } + + if(unlikely(*s == 'e' || *s == 'E')) + return strtold(start, endptr); + + if(unlikely(endptr)) + *endptr = (char *)s; + + if(unlikely(negative)) { + if(unlikely(decimal_digits)) + return -((long double)integer_part + (long double)decimal_part / powl(10.0, decimal_digits)); + else + return -((long double)integer_part); + } + else { + if(unlikely(decimal_digits)) + return (long double)integer_part + (long double)decimal_part / powl(10.0, decimal_digits); + else + return (long double)integer_part; + } +} + #ifdef NETDATA_STRCMP_OVERRIDE #ifdef strcmp #undef strcmp diff --git a/src/log.c b/src/log.c index 855ecaee6..b3dfc73d0 100644 --- a/src/log.c +++ b/src/log.c @@ -23,6 +23,37 @@ void syslog_init(void) { } } +#define LOG_DATE_LENGTH 26 + +static inline void log_date(char *buffer, size_t len) { + if(unlikely(!buffer || !len)) + return; + + time_t t; + struct tm *tmp, tmbuf; + + t = now_realtime_sec(); + tmp = localtime_r(&t, &tmbuf); + + if (tmp == NULL) { + buffer[0] = '\0'; + return; + } + + if (unlikely(strftime(buffer, len, "%Y-%m-%d %H:%M:%S", tmp) == 0)) + buffer[0] = '\0'; + + buffer[len - 1] = '\0'; +} + +static netdata_mutex_t log_mutex = NETDATA_MUTEX_INITIALIZER; +static inline void log_lock() { + netdata_mutex_lock(&log_mutex); +} +static inline void log_unlock() { + netdata_mutex_unlock(&log_mutex); +} + int open_log_file(int fd, FILE **fp, const char *filename, int *enabled_syslog) { int f; @@ -136,8 +167,10 @@ int error_log_limit(int reset) { if(reset) { if(prevented) { - log_date(stderr); - fprintf(stderr, "%s: Resetting logging for process '%s' (prevented %lu logs in the last %ld seconds).\n" + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH); + fprintf(stderr, "%s: %s Resetting logging for process '%s' (prevented %lu logs in the last %ld seconds).\n" + , date , program_name , program_name , prevented @@ -155,8 +188,10 @@ int error_log_limit(int reset) { if(now - start > error_log_throttle_period) { if(prevented) { - log_date(stderr); - fprintf(stderr, "%s: Resuming logging from process '%s' (prevented %lu logs in the last %ld seconds).\n" + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH); + fprintf(stderr, "%s: %s Resuming logging from process '%s' (prevented %lu logs in the last %ld seconds).\n" + , date , program_name , program_name , prevented @@ -175,8 +210,10 @@ int error_log_limit(int reset) { if(counter > error_log_errors_per_period) { if(!prevented) { - log_date(stderr); - fprintf(stderr, "%s: Too many logs (%lu logs in %ld seconds, threshold is set to %lu logs in %ld seconds). Preventing more logs from process '%s' for %ld seconds.\n" + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH); + fprintf(stderr, "%s: %s Too many logs (%lu logs in %ld seconds, threshold is set to %lu logs in %ld seconds). Preventing more logs from process '%s' for %ld seconds.\n" + , date , program_name , counter , now - start @@ -199,38 +236,17 @@ int error_log_limit(int reset) { return 0; } -// ---------------------------------------------------------------------------- -// print the date - -// FIXME -// this should print the date in a buffer the way it -// is now, logs from multiple threads may be multiplexed - -void log_date(FILE *out) -{ - char outstr[26]; - time_t t; - struct tm *tmp, tmbuf; - - t = now_realtime_sec(); - tmp = localtime_r(&t, &tmbuf); - - if (tmp == NULL) return; - if (unlikely(strftime(outstr, sizeof(outstr), "%Y-%m-%d %H:%M:%S", tmp) == 0)) return; - - fprintf(out, "%s: ", outstr); -} - // ---------------------------------------------------------------------------- // debug log -void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) -{ +void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) { va_list args; - log_date(stdout); + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH); + va_start( args, fmt ); - printf("%s: DEBUG (%04lu@%-10.10s:%-15.15s): ", program_name, line, file, function); + printf("%s: %s DEBUG (%04lu@%-10.10s:%-15.15s): ", date, program_name, line, file, function); vprintf(fmt, args); va_end( args ); putchar('\n'); @@ -254,21 +270,26 @@ void info_int( const char *file, const char *function, const unsigned long line, // prevent logging too much if(error_log_limit(0)) return; - log_date(stderr); + if(error_log_syslog) { + va_start( args, fmt ); + vsyslog(LOG_INFO, fmt, args ); + va_end( args ); + } + + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH); + + log_lock(); 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: %s INFO : (%04lu@%-10.10s:%-15.15s): ", date, program_name, line, file, function); + else fprintf(stderr, "%s: %s INFO : ", date, program_name); vfprintf( stderr, fmt, args ); va_end( args ); fputc('\n', stderr); - if(error_log_syslog) { - va_start( args, fmt ); - vsyslog(LOG_INFO, fmt, args ); - va_end( args ); - } + log_unlock(); } // ---------------------------------------------------------------------------- @@ -296,56 +317,67 @@ static const char *strerror_result_string(const char *a, const char *b) { (void) #error "cannot detect the format of function strerror_r()" #endif -void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) -{ +void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) { + // save a copy of errno - just in case this function generates a new error + int __errno = errno; + va_list args; // prevent logging too much if(error_log_limit(0)) return; - log_date(stderr); + if(error_log_syslog) { + va_start( args, fmt ); + vsyslog(LOG_ERR, fmt, args ); + va_end( args ); + } + + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH); + + log_lock(); va_start( args, fmt ); - if(debug_flags) fprintf(stderr, "%s: %s: (%04lu@%-10.10s:%-15.15s): ", program_name, prefix, line, file, function); - else fprintf(stderr, "%s: %s: ", program_name, prefix); + if(debug_flags) fprintf(stderr, "%s: %s %s: (%04lu@%-10.10s:%-15.15s): ", date, program_name, prefix, line, file, function); + else fprintf(stderr, "%s: %s %s: ", date, program_name, prefix); vfprintf( stderr, fmt, args ); va_end( args ); - if(errno) { + if(__errno) { char buf[1024]; - fprintf(stderr, " (errno %d, %s)\n", errno, strerror_result(strerror_r(errno, buf, 1023), buf)); + fprintf(stderr, " (errno %d, %s)\n", __errno, strerror_result(strerror_r(__errno, buf, 1023), buf)); errno = 0; } else fputc('\n', stderr); + log_unlock(); +} + +void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) { + va_list args; + if(error_log_syslog) { va_start( args, fmt ); - vsyslog(LOG_ERR, fmt, args ); + vsyslog(LOG_CRIT, fmt, args ); va_end( args ); } -} -void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) -{ - va_list args; + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH); - log_date(stderr); + log_lock(); va_start( args, fmt ); - if(debug_flags) fprintf(stderr, "%s: FATAL: (%04lu@%-10.10s:%-15.15s): ", program_name, line, file, function); - else fprintf(stderr, "%s: FATAL: ", program_name); + if(debug_flags) fprintf(stderr, "%s: %s FATAL: (%04lu@%-10.10s:%-15.15s): ", date, program_name, line, file, function); + else fprintf(stderr, "%s: %s FATAL: ", date, program_name); vfprintf( stderr, fmt, args ); va_end( args ); perror(" # "); fputc('\n', stderr); - if(error_log_syslog) { - va_start( args, fmt ); - vsyslog(LOG_CRIT, fmt, args ); - va_end( args ); - } + log_unlock(); netdata_cleanup_and_exit(1); } @@ -353,23 +385,29 @@ void fatal_int( const char *file, const char *function, const unsigned long line // ---------------------------------------------------------------------------- // access log -void log_access( const char *fmt, ... ) -{ +void log_access( const char *fmt, ... ) { va_list args; + if(access_log_syslog) { + va_start( args, fmt ); + vsyslog(LOG_INFO, fmt, args ); + va_end( args ); + } + if(stdaccess) { - log_date(stdaccess); + static netdata_mutex_t access_mutex = NETDATA_MUTEX_INITIALIZER; + + netdata_mutex_lock(&access_mutex); + + char date[LOG_DATE_LENGTH]; + log_date(date, LOG_DATE_LENGTH); + fprintf(stdaccess, "%s: ", date); va_start( args, fmt ); vfprintf( stdaccess, fmt, args ); va_end( args ); fputc('\n', stdaccess); - } - if(access_log_syslog) { - va_start( args, fmt ); - vsyslog(LOG_INFO, fmt, args ); - va_end( args ); + netdata_mutex_unlock(&access_mutex); } } - diff --git a/src/log.h b/src/log.h index d8ff0654b..c0414df8d 100644 --- a/src/log.h +++ b/src/log.h @@ -28,6 +28,10 @@ #define D_CONNECT_TO 0x0000000001000000 #define D_RRDHOST 0x0000000002000000 #define D_LOCKS 0x0000000004000000 +#define D_BACKEND 0x0000000008000000 +#define D_STATSD 0x0000000010000000 +#define D_POLLFD 0x0000000020000000 +#define D_STREAM 0x0000000040000000 #define D_SYSTEM 0x8000000000000000 //#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS) @@ -56,16 +60,22 @@ extern int error_log_limit(int reset); extern void open_all_log_files(); extern void reopen_all_log_files(); +static inline void debug_dummy(void) {} + #define error_log_limit_reset() do { error_log_throttle_period = error_log_throttle_period_backup; error_log_limit(1); } while(0) #define error_log_limit_unlimited() do { error_log_throttle_period = 0; } while(0) +#ifdef NETDATA_INTERNAL_CHECKS #define debug(type, args...) do { if(unlikely(debug_flags & type)) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0) +#else +#define debug(type, args...) debug_dummy() +#endif + #define info(args...) info_int(__FILE__, __FUNCTION__, __LINE__, ##args) #define infoerr(args...) error_int("INFO", __FILE__, __FUNCTION__, __LINE__, ##args) #define error(args...) error_int("ERROR", __FILE__, __FUNCTION__, __LINE__, ##args) #define fatal(args...) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args) -extern void log_date(FILE *out); extern void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5); extern void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5); extern void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(5, 6); diff --git a/src/main.c b/src/main.c index a72585e28..bf5d787ad 100644 --- a/src/main.c +++ b/src/main.c @@ -9,8 +9,8 @@ void netdata_cleanup_and_exit(int ret) { debug(D_EXIT, "Called: netdata_cleanup_and_exit()"); - // save the database - rrdhost_save_all(); + // cleanup the database + rrdhost_cleanup_all(); // unlink the pid if(pidfile[0]) { @@ -56,6 +56,7 @@ struct netdata_static_thread static_threads[] = { {"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}, + {"statsd", NULL, NULL, 1, NULL, NULL, statsd_main}, {NULL, NULL, NULL, 0, NULL, NULL, NULL} }; @@ -66,14 +67,16 @@ void web_server_threading_selection(void) { 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) + for (i = 0; static_threads[i].name; i++) { + if (static_threads[i].start_routine == socket_listen_main_multi_threaded) static_threads[i].enabled = multi_threaded; - if(static_threads[i].start_routine == socket_listen_main_single_threaded) + if (static_threads[i].start_routine == socket_listen_main_single_threaded) static_threads[i].enabled = single_threaded; } +} +void web_server_config_options(void) { web_client_timeout = (int) config_get_number(CONFIG_SECTION_WEB, "disconnect idle clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS); 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); @@ -299,6 +302,8 @@ void help(int exitcode) { " -W stacksize=N Set the stacksize (in bytes).\n\n" " -W debug_flags=N Set runtime tracing to debug.log.\n\n" " -W unittest Run internal unittests and exit.\n\n" + " -W set section option value\n" + " set netdata.conf option from the command line.\n\n" " -W simple-pattern pattern string\n" " Check if string matches pattern and exit.\n\n" ); @@ -405,6 +410,9 @@ static void backwards_compatible_config() { config_move(CONFIG_SECTION_GLOBAL, "web files group", CONFIG_SECTION_WEB, "web files group"); + + config_move(CONFIG_SECTION_BACKEND, "opentsdb host tags", + CONFIG_SECTION_BACKEND, "host tags"); } static void get_netdata_configured_variables() { @@ -420,8 +428,6 @@ static void get_netdata_configured_variables() { 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 @@ -502,7 +508,9 @@ void set_global_environment() { // 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); + const char *tz = getenv("TZ"); + if(!tz || !*tz) + setenv("TZ", config_get(CONFIG_SECTION_GLOBAL, "TZ environment variable", ":/etc/localtime"), 0); // set the path we need char path[1024 + 1], *p = getenv("PATH"); @@ -625,10 +633,12 @@ int main(int argc, char **argv) { { char* stacksize_string = "stacksize="; char* debug_flags_string = "debug_flags="; + if(strcmp(optarg, "unittest") == 0) { - default_rrd_update_every = 1; - default_rrd_memory_mode = RRD_MEMORY_MODE_RAM; - if(!config_loaded) config_load(NULL, 0); + if(unit_test_str2ld()) exit(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; @@ -691,9 +701,71 @@ int main(int argc, char **argv) { config_set(CONFIG_SECTION_GLOBAL, "debug flags", optarg); debug_flags = strtoull(optarg, NULL, 0); } + else if(strcmp(optarg, "set") == 0) { + if(optind + 3 > argc) { + fprintf(stderr, "%s", "\nUSAGE: -W set 'section' 'key' 'value'\n\n" + " Overwrites settings of netdata.conf.\n" + "\n" + " These options interact with: -c netdata.conf\n" + " If -c netdata.conf is given on the command line,\n" + " before -W set... the user may overwrite command\n" + " line parameters at netdata.conf\n" + " If -c netdata.conf is given after (or missing)\n" + " -W set... the user cannot overwrite the command line\n" + " parameters." + "\n" + ); + exit(1); + } + const char *section = argv[optind]; + const char *key = argv[optind + 1]; + const char *value = argv[optind + 2]; + optind += 3; + + // set this one as the default + // only if it is not already set in the config file + // so the caller can use -c netdata.conf before or + // after this parameter to prevent or allow overwriting + // variables at netdata.conf + config_set_default(section, key, value); + + // fprintf(stderr, "SET section '%s', key '%s', value '%s'\n", section, key, value); + } + else if(strcmp(optarg, "get") == 0) { + if(optind + 3 > argc) { + fprintf(stderr, "%s", "\nUSAGE: -W get 'section' 'key' 'value'\n\n" + " Prints settings of netdata.conf.\n" + "\n" + " These options interact with: -c netdata.conf\n" + " -c netdata.conf has to be given before -W get.\n" + "\n" + ); + exit(1); + } + + if(!config_loaded) { + fprintf(stderr, "warning: no configuration file has been loaded. Use -c CONFIG_FILE, before -W get. Using default config.\n"); + config_load(NULL, 0); + } + + backwards_compatible_config(); + get_netdata_configured_variables(); + + const char *section = argv[optind]; + const char *key = argv[optind + 1]; + const char *def = argv[optind + 2]; + const char *value = config_get(section, key, def); + printf("%s\n", value); + exit(0); + } + else { + fprintf(stderr, "Unknown -W parameter '%s'\n", optarg); + help(1); + } } break; default: /* ? */ + fprintf(stderr, "Unknown parameter '%c'\n", opt); help(1); break; } @@ -875,8 +947,10 @@ int main(int argc, char **argv) { // -------------------------------------------------------------------- // create the listening sockets + web_server_threading_selection(); + if(web_server_mode != WEB_SERVER_MODE_NONE) - create_listen_sockets(); + api_listen_sockets_setup(); } // initialize the log files @@ -928,7 +1002,7 @@ int main(int argc, char **argv) { // ------------------------------------------------------------------------ // spawn the threads - web_server_threading_selection(); + web_server_config_options(); for (i = 0; static_threads[i].name != NULL ; i++) { struct netdata_static_thread *st = &static_threads[i]; diff --git a/src/plugin_freebsd.c b/src/plugin_freebsd.c index 31ab6e0c4..020fdb41c 100644 --- a/src/plugin_freebsd.c +++ b/src/plugin_freebsd.c @@ -53,6 +53,12 @@ static struct freebsd_module { // network interfaces metrics { .name = "getifaddrs", .dim = "getifaddrs", .enabled = 1, .func = do_getifaddrs }, + // ZFS metrics + { .name = "kstat.zfs.misc.arcstats", .dim = "arcstats", .enabled = 1, .func = do_kstat_zfs_misc_arcstats }, + + // ipfw metrics + { .name = "ipfw", .dim = "ipfw", .enabled = 1, .func = do_ipfw }, + // the terminator of this array { .name = NULL, .dim = NULL, .enabled = 0, .func = NULL } }; diff --git a/src/plugin_freebsd.h b/src/plugin_freebsd.h index 166c64338..541bf852f 100644 --- a/src/plugin_freebsd.h +++ b/src/plugin_freebsd.h @@ -3,6 +3,12 @@ #include +#define KILO_FACTOR 1024 +#define MEGA_FACTOR 1048576 // 1024 * 1024 +#define GIGA_FACTOR 1073741824 // 1024 * 1024 * 1024 + +#define MAX_INT_DIGITS 10 // maximum number of digits for int + void *freebsd_main(void *ptr); extern int freebsd_plugin_init(); @@ -35,6 +41,8 @@ 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); +extern int do_kstat_zfs_misc_arcstats(int update_every, usec_t dt); +extern int do_ipfw(int update_every, usec_t dt); #define GETSYSCTL_MIB(name, mib) getsysctl_mib(name, mib, sizeof(mib)/sizeof(int)) diff --git a/src/plugin_idlejitter.c b/src/plugin_idlejitter.c index 2ed78160c..89f490233 100644 --- a/src/plugin_idlejitter.c +++ b/src/plugin_idlejitter.c @@ -13,41 +13,71 @@ void *cpuidlejitter_main(void *ptr) { if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); - int sleep_ms = (int) config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS); - if(sleep_ms <= 0) { + usec_t sleep_ut = config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS) * USEC_PER_MS; + if(sleep_ut <= 0) { config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS); - sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS; - } - - RRDSET *st = rrdset_find_localhost("system.idlejitter"); - if(!st) { - 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); + sleep_ut = CPU_IDLEJITTER_SLEEP_TIME_MS * USEC_PER_MS; } + RRDSET *st = rrdset_create_localhost( + "system" + , "idlejitter" + , NULL + , "processes" + , NULL + , "CPU Idle Jitter" + , "microseconds lost/s" + , 9999 + , localhost->rrd_update_every + , RRDSET_TYPE_AREA + ); + RRDDIM *rd_min = rrddim_add(st, "min", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + RRDDIM *rd_max = rrddim_add(st, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + RRDDIM *rd_avg = rrddim_add(st, "average", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + usec_t update_every_ut = localhost->rrd_update_every * USEC_PER_SEC; struct timeval before, after; unsigned long long counter; for(counter = 0; 1 ;counter++) { - usec_t usec = 0, susec = 0; + int iterations = 0; + usec_t error_total = 0, + error_min = 0, + error_max = 0, + elapsed = 0; if(netdata_exit) break; - while(susec < (localhost->rrd_update_every * USEC_PER_SEC)) { - + while(elapsed < update_every_ut) { now_monotonic_timeval(&before); - sleep_usec(sleep_ms * 1000); + sleep_usec(sleep_ut); now_monotonic_timeval(&after); - // calculate the time it took for a full loop - usec = dt_usec(&after, &before); - susec += usec; + usec_t dt = dt_usec(&after, &before); + elapsed += dt; + + usec_t error = dt - sleep_ut; + error_total += error; + + if(unlikely(!iterations)) + error_min = error; + else if(error < error_min) + error_min = error; + + if(error > error_max) + error_max = error; + + iterations++; } - usec -= (sleep_ms * 1000); - if(counter) rrdset_next(st); - rrddim_set(st, "jitter", usec); - rrdset_done(st); + if(netdata_exit) break; + + if(iterations) { + if (likely(counter)) rrdset_next(st); + rrddim_set_by_pointer(st, rd_min, error_min); + rrddim_set_by_pointer(st, rd_max, error_max); + rrddim_set_by_pointer(st, rd_avg, error_total / iterations); + rrdset_done(st); + } } info("IDLEJITTER thread exiting"); diff --git a/src/plugin_proc.c b/src/plugin_proc.c index 2ca77491d..e64f57398 100644 --- a/src/plugin_proc.c +++ b/src/plugin_proc.c @@ -32,7 +32,7 @@ static struct proc_module { // network metrics { .name = "/proc/net/dev", .dim = "netdev", .func = do_proc_net_dev }, - { .name = "/proc/net/netstat", .dim = "netstat", .func = do_proc_net_netstat }, + { .name = "/proc/net/netstat", .dim = "netstat", .func = do_proc_net_netstat }, // this has to be before /proc/net/snmp, because there is a shared metric { .name = "/proc/net/snmp", .dim = "snmp", .func = do_proc_net_snmp }, { .name = "/proc/net/snmp6", .dim = "snmp6", .func = do_proc_net_snmp6 }, { .name = "/proc/net/softnet_stat", .dim = "softnet", .func = do_proc_net_softnet_stat }, @@ -49,6 +49,9 @@ static struct proc_module { { .name = "/proc/net/rpc/nfsd", .dim = "nfsd", .func = do_proc_net_rpc_nfsd }, { .name = "/proc/net/rpc/nfs", .dim = "nfs", .func = do_proc_net_rpc_nfs }, + // ZFS metrics + { .name = "/proc/spl/kstat/zfs/arcstats", .dim = "zfs_arcstats", .func = do_proc_spl_kstat_zfs_arcstats }, + // IPC metrics { .name = "ipc", .dim = "ipc", .func = do_ipc }, diff --git a/src/plugin_proc.h b/src/plugin_proc.h index 5dee7853c..688b23de9 100644 --- a/src/plugin_proc.h +++ b/src/plugin_proc.h @@ -25,7 +25,11 @@ extern int do_proc_net_softnet_stat(int update_every, usec_t dt); extern int do_proc_uptime(int update_every, usec_t dt); extern int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt); extern int do_proc_sys_devices_system_node(int update_every, usec_t dt); +extern int do_proc_spl_kstat_zfs_arcstats(int update_every, usec_t dt); extern int get_numa_node_count(void); +// metrics that need to be shared among data collectors +extern unsigned long long tcpext_TCPSynRetrans; + #endif /* NETDATA_PLUGIN_PROC_H */ diff --git a/src/plugin_proc_diskspace.c b/src/plugin_proc_diskspace.c index 37133e044..750086a2c 100644 --- a/src/plugin_proc_diskspace.c +++ b/src/plugin_proc_diskspace.c @@ -45,7 +45,7 @@ struct mount_point_metadata { 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) +#define rrdset_obsolete_and_pointer_null(st) do { if(st) { rrdset_is_obsolete(st); st = NULL; } } while(st) int mount_point_cleanup(void *entry, void *data) { (void)data; @@ -125,6 +125,33 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { def_inodes = CONFIG_BOOLEAN_NO; } + // check if the mount point is a directory #2407 + { + struct stat bs; + if(stat(mi->mount_point, &bs) == -1) { + error("DISKSPACE: Cannot stat() mount point '%s' (disk '%s', filesystem '%s', root '%s')." + , mi->mount_point + , disk + , mi->filesystem?mi->filesystem:"" + , mi->root?mi->root:"" + ); + def_space = CONFIG_BOOLEAN_NO; + def_inodes = CONFIG_BOOLEAN_NO; + } + else { + if((bs.st_mode & S_IFMT) != S_IFDIR) { + error("DISKSPACE: Mount point '%s' (disk '%s', filesystem '%s', root '%s') is not a directory." + , mi->mount_point + , disk + , mi->filesystem?mi->filesystem:"" + , mi->root?mi->root:"" + ); + def_space = CONFIG_BOOLEAN_NO; + def_inodes = CONFIG_BOOLEAN_NO; + } + } + } + do_space = config_get_boolean_ondemand(var_name, "space usage", def_space); do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", def_inodes); @@ -161,7 +188,7 @@ 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) { if(!m->shown_error) { - error("Failed statvfs() for '%s' (disk '%s', filesystem '%s', root '%s')" + error("DISKSPACE: failed to statvfs() mount point '%s' (disk '%s', filesystem '%s', root '%s')" , mi->mount_point , disk , mi->filesystem?mi->filesystem:"" @@ -188,7 +215,7 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { #ifdef NETDATA_INTERNAL_CHECKS if(unlikely(btotal != bavail + breserved_root + bused)) - error("Disk block statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mi->mount_point, disk, (unsigned long long)btotal, (unsigned long long)bavail, (unsigned long long)breserved_root, (unsigned long long)bused); + error("DISKSPACE: disk block statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mi->mount_point, disk, (unsigned long long)btotal, (unsigned long long)bavail, (unsigned long long)breserved_root, (unsigned long long)bused); #endif // -------------------------------------------------------------------------- @@ -201,7 +228,7 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) { #ifdef NETDATA_INTERNAL_CHECKS if(unlikely(btotal != bavail + breserved_root + bused)) - error("Disk inode statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mi->mount_point, disk, (unsigned long long)ftotal, (unsigned long long)favail, (unsigned long long)freserved_root, (unsigned long long)fused); + error("DISKSPACE: disk inode statistics for '%s' (disk '%s') do not sum up: total = %llu, available = %llu, reserved = %llu, used = %llu", mi->mount_point, disk, (unsigned long long)ftotal, (unsigned long long)favail, (unsigned long long)freserved_root, (unsigned long long)fused); #endif // -------------------------------------------------------------------------- @@ -294,10 +321,10 @@ void *proc_diskspace_main(void *ptr) { info("DISKSPACE thread created with task id %d", gettid()); if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) - error("Cannot set pthread cancel type to DEFERRED."); + error("DISKSPACE: Cannot set pthread cancel type to DEFERRED."); if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) - error("Cannot set pthread cancel state to ENABLE."); + error("DISKSPACE: Cannot set pthread cancel state to ENABLE."); int vdo_cpu_netdata = config_get_boolean("plugin:proc", "netdata server resources", 1); @@ -334,7 +361,7 @@ void *proc_diskspace_main(void *ptr) { struct mountinfo *mi; for(mi = disk_mountinfo_root; mi; mi = mi->next) { - if(unlikely(mi->flags & (MOUNTINFO_IS_DUMMY | MOUNTINFO_IS_BIND | MOUNTINFO_IS_SAME_DEV | MOUNTINFO_NO_STAT | MOUNTINFO_NO_SIZE))) + if(unlikely(mi->flags & (MOUNTINFO_IS_DUMMY | MOUNTINFO_IS_BIND))) continue; do_disk_space_stats(mi, update_every); diff --git a/src/plugin_tc.c b/src/plugin_tc.c index 7dcfedb33..6bf5782a9 100644 --- a/src/plugin_tc.c +++ b/src/plugin_tc.c @@ -713,7 +713,7 @@ static inline void tc_device_free_all() tc_device_free(tc_device_root); } -#define MAX_WORDS 20 +#define PLUGINSD_MAX_WORDS 20 static inline int tc_space(char c) { switch(c) { @@ -779,7 +779,7 @@ void *tc_main(void *ptr) { RRDSET *stcpu = NULL, *sttime = NULL; char buffer[TC_LINE_MAX+1] = ""; - char *words[MAX_WORDS] = { NULL }; + char *words[PLUGINSD_MAX_WORDS] = { NULL }; uint32_t BEGIN_HASH = simple_hash("BEGIN"); uint32_t END_HASH = simple_hash("END"); @@ -822,7 +822,7 @@ void *tc_main(void *ptr) { buffer[TC_LINE_MAX] = '\0'; // debug(D_TC_LOOP, "TC: read '%s'", buffer); - tc_split_words(buffer, words, MAX_WORDS); + tc_split_words(buffer, words, PLUGINSD_MAX_WORDS); if(unlikely(!words[0] || !*words[0])) { // debug(D_TC_LOOP, "empty line"); diff --git a/src/plugins_d.c b/src/plugins_d.c index 7fa19eaf0..9eb102770 100644 --- a/src/plugins_d.c +++ b/src/plugins_d.c @@ -2,8 +2,6 @@ struct plugind *pluginsd_root = NULL; -#define MAX_WORDS 20 - static inline int pluginsd_space(char c) { switch(c) { case ' ': @@ -18,7 +16,8 @@ static inline int pluginsd_space(char c) { } } -static int pluginsd_split_words(char *str, char **words, int max_words) { +// split a text into words, respecting quotes +inline int pluginsd_split_words(char *str, char **words, int max_words) { char *s = str, quote = 0; int i = 0, j; @@ -95,14 +94,13 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int 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"); + char *words[PLUGINSD_MAX_WORDS] = { NULL }; + uint32_t BEGIN_HASH = simple_hash(PLUGINSD_KEYWORD_BEGIN); + uint32_t END_HASH = simple_hash(PLUGINSD_KEYWORD_END); + uint32_t FLUSH_HASH = simple_hash(PLUGINSD_KEYWORD_FLUSH); + uint32_t CHART_HASH = simple_hash(PLUGINSD_KEYWORD_CHART); + uint32_t DIMENSION_HASH = simple_hash(PLUGINSD_KEYWORD_DIMENSION); + uint32_t DISABLE_HASH = simple_hash(PLUGINSD_KEYWORD_DISABLE); RRDSET *st = NULL; uint32_t hash; @@ -130,7 +128,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line); - int w = pluginsd_split_words(line, words, MAX_WORDS); + int w = pluginsd_split_words(line, words, PLUGINSD_MAX_WORDS); char *s = words[0]; if(unlikely(!s || !*s || !w)) { // debug(D_PLUGINSD, "PLUGINSD: empty line"); @@ -159,9 +157,18 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int 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:""); - if(value) rrddim_set(st, dimension, strtoll(value, NULL, 0)); + if(value) { + RRDDIM *rd = rrddim_find(st, dimension); + if(unlikely(!rd)) { + error("PLUGINSD: '%s' is requesting a SET to dimension with id '%s' on stats '%s' (%s) on host '%s', which does not exist. Disabling it.", cd->fullfilename, dimension, st->name, st->id, st->rrdhost->hostname); + enabled = 0; + break; + } + else + rrddim_set_by_pointer(st, rd, strtoll(value, NULL, 0)); + } } - else if(likely(hash == BEGIN_HASH && !strcmp(s, "BEGIN"))) { + else if(likely(hash == BEGIN_HASH && !strcmp(s, PLUGINSD_KEYWORD_BEGIN))) { char *id = words[1]; char *microseconds_txt = words[2]; @@ -191,7 +198,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int else rrdset_next(st); } } - else if(likely(hash == END_HASH && !strcmp(s, "END"))) { + else if(likely(hash == END_HASH && !strcmp(s, PLUGINSD_KEYWORD_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; @@ -205,28 +212,11 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int count++; } -/* else if(likely(hash == HOST_HASH && !strcmp(s, "HOST"))) { - char *guid = words[1]; - char *hostname = words[2]; - - 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; - } - 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; - } - - host = rrdhost_find_or_create(hostname, guid); - } */ - else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) { + else if(likely(hash == FLUSH_HASH && !strcmp(s, PLUGINSD_KEYWORD_FLUSH))) { debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename); st = NULL; } - else if(likely(hash == CHART_HASH && !strcmp(s, "CHART"))) { + else if(likely(hash == CHART_HASH && !strcmp(s, PLUGINSD_KEYWORD_CHART))) { int noname = 0; st = NULL; @@ -247,6 +237,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int char *chart = words[7]; char *priority_s = words[8]; char *update_every_s = words[9]; + char *options = words[10]; 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); @@ -284,8 +275,25 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int cd->update_every = update_every; } else debug(D_PLUGINSD, "PLUGINSD: Chart '%s' already exists. Not adding it again.", st->id); + + if(options && *options) { + if(strstr(options, "obsolete")) + rrdset_is_obsolete(st); + else + rrdset_isnot_obsolete(st); + + if(strstr(options, "detail")) + rrdset_flag_set(st, RRDSET_FLAG_DETAIL); + else + rrdset_flag_clear(st, RRDSET_FLAG_DETAIL); + + if(strstr(options, "store_first")) + rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST); + else + rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST); + } } - else if(likely(hash == DIMENSION_HASH && !strcmp(s, "DIMENSION"))) { + else if(likely(hash == DIMENSION_HASH && !strcmp(s, PLUGINSD_KEYWORD_DIMENSION))) { char *id = words[1]; char *name = words[2]; char *algorithm = words[3]; @@ -326,21 +334,16 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int , 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); - } + RRDDIM *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 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"))) { + else if(unlikely(hash == DISABLE_HASH && !strcmp(s, PLUGINSD_KEYWORD_DISABLE))) { info("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename); enabled = 0; break; diff --git a/src/plugins_d.h b/src/plugins_d.h index d34c4030c..595a515c4 100644 --- a/src/plugins_d.h +++ b/src/plugins_d.h @@ -4,7 +4,16 @@ #define PLUGINSD_FILE_SUFFIX ".plugin" #define PLUGINSD_FILE_SUFFIX_LEN strlen(PLUGINSD_FILE_SUFFIX) #define PLUGINSD_CMD_MAX (FILENAME_MAX*2) + +#define PLUGINSD_KEYWORD_CHART "CHART" +#define PLUGINSD_KEYWORD_DIMENSION "DIMENSION" +#define PLUGINSD_KEYWORD_BEGIN "BEGIN" +#define PLUGINSD_KEYWORD_END "END" +#define PLUGINSD_KEYWORD_FLUSH "FLUSH" +#define PLUGINSD_KEYWORD_DISABLE "DISABLE" + #define PLUGINSD_LINE_MAX 1024 +#define PLUGINSD_MAX_WORDS 20 struct plugind { char id[CONFIG_MAX_NAME+1]; // config node id @@ -35,5 +44,6 @@ 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); +extern int pluginsd_split_words(char *str, char **words, int max_words); #endif /* NETDATA_PLUGINS_D_H */ diff --git a/src/proc_diskstats.c b/src/proc_diskstats.c index a1b4072dd..d3fed5a6d 100644 --- a/src/proc_diskstats.c +++ b/src/proc_diskstats.c @@ -10,7 +10,8 @@ #define DELAULT_EXLUDED_DISKS "loop* ram*" static struct disk { - char *disk; // the name of the disk (sda, sdb, etc) + char *disk; // the name of the disk (sda, sdb, etc, after being looked up) + char *device; // the device of the disk (before being looked up) unsigned long major; unsigned long minor; int sector_size; @@ -44,12 +45,75 @@ static struct disk { 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) +#define rrdset_obsolete_and_pointer_null(st) do { if(st) { rrdset_is_obsolete(st); st = NULL; } } while(st) + +static char *path_to_get_hw_sector_size = NULL; +static char *path_to_get_hw_sector_size_partitions = NULL; +static char *path_to_find_block_device = NULL; +static char *path_to_device_mapper = NULL; + +static inline char *get_disk_name(unsigned long major, unsigned long minor, char *disk) { + static int enabled = 1; + + if(!enabled) goto cleanup; + + char filename[FILENAME_MAX + 1]; + char link[FILENAME_MAX + 1]; + + DIR *dir = opendir(path_to_device_mapper); + if (!dir) { + error("DEVICE-MAPPER ('%s', %lu:%lu): Cannot open directory '%s'. Disabling device-mapper support.", disk, major, minor, path_to_device_mapper); + enabled = 0; + goto cleanup; + } + + struct dirent *de = NULL; + while ((de = readdir(dir))) { + if(de->d_type != DT_LNK) continue; + + snprintfz(filename, FILENAME_MAX, "%s/%s", path_to_device_mapper, de->d_name); + ssize_t len = readlink(filename, link, FILENAME_MAX); + if(len <= 0) { + error("DEVICE-MAPPER ('%s', %lu:%lu): Cannot read link '%s'.", disk, major, minor, filename); + continue; + } + + link[len] = '\0'; + if(link[0] != '/') + snprintfz(filename, FILENAME_MAX, "%s/%s", path_to_device_mapper, link); + else + strncpyz(filename, link, FILENAME_MAX); + + struct stat sb; + if(stat(filename, &sb) == -1) { + error("DEVICE-MAPPER ('%s', %lu:%lu): Cannot stat() file '%s'.", disk, major, minor, filename); + continue; + } + + if((sb.st_mode & S_IFMT) != S_IFBLK) { + // info("DEVICE-MAPPER ('%s', %lu:%lu): file '%s' is not a block device.", disk, major, minor, filename); + continue; + } + + if(major(sb.st_rdev) != major || minor(sb.st_rdev) != minor) { + // info("DEVICE-MAPPER ('%s', %lu:%lu): filename '%s' does not match %lu:%lu.", disk, major, minor, filename, (unsigned long)major(sb.st_rdev), (unsigned long)minor(sb.st_rdev)); + continue; + } + + // info("DEVICE-MAPPER ('%s', %lu:%lu): filename '%s' matches.", disk, major, minor, filename); + + strncpy(link, de->d_name, FILENAME_MAX); + netdata_fix_chart_name(link); + disk = link; + break; + } + closedir(dir); + +cleanup: + return strdupz(disk); +} static struct disk *get_disk(unsigned long major, unsigned long minor, char *disk) { - static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = ""; - static char path_to_get_hw_sector_size_partitions[FILENAME_MAX + 1] = ""; - static char path_find_block_device[FILENAME_MAX + 1] = ""; static struct mountinfo *disk_mountinfo_root = NULL; struct disk *d; @@ -66,7 +130,8 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis // create a new disk structure d = (struct disk *)callocz(1, sizeof(struct disk)); - d->disk = strdupz(disk); + d->disk = get_disk_name(major, minor, disk); + d->device = strdupz(disk); d->major = major; d->minor = minor; d->type = DISK_TYPE_PHYSICAL; // Default type. Changed later if not correct. @@ -83,27 +148,17 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis last->next = d; } - // ------------------------------------------------------------------------ - // find the type of the device - - char buffer[FILENAME_MAX + 1]; - - // get the default path for finding info about the block device - if(unlikely(!path_find_block_device[0])) { - snprintfz(buffer, FILENAME_MAX, "%s%s", 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 // by checking if /sys/dev/block/MAJOR:MINOR/partition is readable. - snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "partition"); + char buffer[FILENAME_MAX + 1]; + snprintfz(buffer, FILENAME_MAX, path_to_find_block_device, major, minor, "partition"); if(likely(access(buffer, R_OK) == 0)) { d->type = DISK_TYPE_PARTITION; } else { // find if it is a container // by checking if /sys/dev/block/MAJOR:MINOR/slaves has entries - snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "slaves/"); + snprintfz(buffer, FILENAME_MAX, path_to_find_block_device, major, minor, "slaves/"); DIR *dirp = opendir(buffer); if(likely(dirp != NULL)) { struct dirent *dp; @@ -143,18 +198,9 @@ 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", 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", 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)); - } - { char tf[FILENAME_MAX + 1], *t; - strncpyz(tf, d->disk, FILENAME_MAX); + strncpyz(tf, d->device, FILENAME_MAX); // replace all / with ! for(t = tf; *t ;t++) @@ -173,15 +219,15 @@ static struct disk *get_disk(unsigned long major, unsigned long minor, char *dis if(likely(tmp)) { d->sector_size = str2i(tmp); if(unlikely(d->sector_size <= 0)) { - error("Invalid sector size %d for device %s in %s. Assuming 512.", d->sector_size, d->disk, buffer); + error("Invalid sector size %d for device %s in %s. Assuming 512.", d->sector_size, d->device, buffer); d->sector_size = 512; } } - else error("Cannot read data for sector size for device %s from %s. Assuming 512.", d->disk, buffer); + else error("Cannot read data for sector size for device %s from %s. Assuming 512.", d->device, buffer); fclose(fpss); } - else error("Cannot read sector size for device %s from %s. Assuming 512.", d->disk, buffer); + else error("Cannot read sector size for device %s from %s. Assuming 512.", d->device, buffer); } return d; @@ -230,6 +276,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { globals_initialized = 0; if(unlikely(!globals_initialized)) { + globals_initialized = 1; + 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); @@ -243,7 +291,19 @@ int do_proc_diskstats(int update_every, usec_t dt) { 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; + char buffer[FILENAME_MAX + 1]; + + snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/dev/block/%lu:%lu/%s"); + path_to_find_block_device = config_get(CONFIG_SECTION_DISKSTATS, "path to get block device infos", buffer); + + snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/block/%s/queue/hw_sector_size"); + path_to_get_hw_sector_size = config_get(CONFIG_SECTION_DISKSTATS, "path to get h/w sector size", buffer); + + snprintfz(buffer, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/dev/block/%lu:%lu/subsystem/%s/../queue/hw_sector_size"); + path_to_get_hw_sector_size_partitions = config_get(CONFIG_SECTION_DISKSTATS, "path to get h/w sector size for partitions", buffer); + + snprintfz(buffer, FILENAME_MAX, "%s/dev/mapper", netdata_configured_host_prefix); + path_to_device_mapper = config_get(CONFIG_SECTION_DISKSTATS, "path to device mapper", buffer); } // -------------------------------------------------------------------------- @@ -339,7 +399,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { // Set its family based on mount point char *family = d->mount_point; - if(!family) family = disk; + if(!family) family = d->disk; // -------------------------------------------------------------------------- @@ -359,11 +419,11 @@ int do_proc_diskstats(int update_every, usec_t dt) { int def_enable = global_enable_new_disks_detected_at_runtime; - if(def_enable != CONFIG_BOOLEAN_NO && simple_pattern_matches(excluded_disks, disk)) + if(def_enable != CONFIG_BOOLEAN_NO && (simple_pattern_matches(excluded_disks, d->device) || simple_pattern_matches(excluded_disks, d->disk))) def_enable = CONFIG_BOOLEAN_NO; char var_name[4096 + 1]; - snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", disk); + snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", d->disk); def_enable = config_get_boolean_ondemand(var_name, "enable", def_enable); if(unlikely(def_enable == CONFIG_BOOLEAN_NO)) { @@ -449,8 +509,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_io)) { d->st_io = rrdset_create_localhost( RRD_TYPE_DISK - , disk - , NULL + , d->device + , d->disk , family , "disk.io" , "Disk I/O Bandwidth" @@ -478,8 +538,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_ops)) { d->st_ops = rrdset_create_localhost( "disk_ops" - , disk - , NULL + , d->device + , d->disk , family , "disk.ops" , "Disk Completed I/O Operations" @@ -509,8 +569,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_qops)) { d->st_qops = rrdset_create_localhost( "disk_qops" - , disk - , NULL + , d->device + , d->disk , family , "disk.qops" , "Disk Current I/O Operations" @@ -538,8 +598,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_backlog)) { d->st_backlog = rrdset_create_localhost( "disk_backlog" - , disk - , NULL + , d->device + , d->disk , family , "disk.backlog" , "Disk Backlog" @@ -567,8 +627,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_util)) { d->st_util = rrdset_create_localhost( "disk_util" - , disk - , NULL + , d->device + , d->disk , family , "disk.util" , "Disk Utilization Time" @@ -596,8 +656,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_mops)) { d->st_mops = rrdset_create_localhost( "disk_mops" - , disk - , NULL + , d->device + , d->disk , family , "disk.mops" , "Disk Merged Operations" @@ -627,8 +687,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_iotime)) { d->st_iotime = rrdset_create_localhost( "disk_iotime" - , disk - , NULL + , d->device + , d->disk , family , "disk.iotime" , "Disk Total I/O Time" @@ -661,8 +721,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_await)) { d->st_await = rrdset_create_localhost( "disk_await" - , disk - , NULL + , d->device + , d->disk , family , "disk.await" , "Average Completed I/O Operation Time" @@ -690,8 +750,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_avgsz)) { d->st_avgsz = rrdset_create_localhost( "disk_avgsz" - , disk - , NULL + , d->device + , d->disk , family , "disk.avgsz" , "Average Completed I/O Operation Bandwidth" @@ -719,8 +779,8 @@ int do_proc_diskstats(int update_every, usec_t dt) { if(unlikely(!d->st_svctm)) { d->st_svctm = rrdset_create_localhost( "disk_svctm" - , disk - , NULL + , d->device + , d->disk , family , "disk.svctm" , "Average Service Time" @@ -769,6 +829,7 @@ int do_proc_diskstats(int update_every, usec_t dt) { } freez(t->disk); + freez(t->device); freez(t->mount_point); freez(t); } diff --git a/src/proc_loadavg.c b/src/proc_loadavg.c index e7863f114..a48801b37 100644 --- a/src/proc_loadavg.c +++ b/src/proc_loadavg.c @@ -68,9 +68,10 @@ int do_proc_loadavg(int update_every, usec_t dt) { rrddim_set(load_chart, "load5", (collected_number) (load5 * 1000)); rrddim_set(load_chart, "load15", (collected_number) (load15 * 1000)); rrdset_done(load_chart); - } - next_loadavg_dt = load_chart->update_every * USEC_PER_SEC; + next_loadavg_dt = load_chart->update_every * USEC_PER_SEC; + } + else next_loadavg_dt = MIN_LOADAVG_UPDATE_EVERY * USEC_PER_SEC; } else next_loadavg_dt -= dt; diff --git a/src/proc_net_dev.c b/src/proc_net_dev.c index 1b00758d5..ee7588990 100644 --- a/src/proc_net_dev.c +++ b/src/proc_net_dev.c @@ -73,13 +73,13 @@ 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); + if(d->st_bandwidth) rrdset_is_obsolete(d->st_bandwidth); + if(d->st_packets) rrdset_is_obsolete(d->st_packets); + if(d->st_errors) rrdset_is_obsolete(d->st_errors); + if(d->st_drops) rrdset_is_obsolete(d->st_drops); + if(d->st_fifo) rrdset_is_obsolete(d->st_fifo); + if(d->st_compressed) rrdset_is_obsolete(d->st_compressed); + if(d->st_events) rrdset_is_obsolete(d->st_events); netdev_added--; freez(d->name); diff --git a/src/proc_net_netstat.c b/src/proc_net_netstat.c index 2677a6c17..322e51d13 100644 --- a/src/proc_net_netstat.c +++ b/src/proc_net_netstat.c @@ -1,5 +1,7 @@ #include "common.h" +unsigned long long tcpext_TCPSynRetrans; + static void parse_line_pair(procfile *ff, ARL_BASE *base, size_t header_line, size_t values_line) { size_t hwords = procfile_linewords(ff, header_line); size_t vwords = procfile_linewords(ff, values_line); @@ -94,6 +96,9 @@ int do_proc_net_netstat(int update_every, usec_t dt) { // IPv4 TCP memory pressures static unsigned long long tcpext_TCPMemoryPressures = 0; + // shared: tcpext_TCPSynRetrans + + if(unlikely(!arl_ipext)) { hash_ipext = simple_hash("IpExt"); hash_tcpext = simple_hash("TcpExt"); @@ -191,6 +196,9 @@ int do_proc_net_netstat(int update_every, usec_t dt) { if(do_tcpext_memory != CONFIG_BOOLEAN_NO) { arl_expect(arl_tcpext, "TCPMemoryPressures", &tcpext_TCPMemoryPressures); } + + // shared metrics + arl_expect(arl_tcpext, "TCPSynRetrans", &tcpext_TCPSynRetrans); } if(unlikely(!ff)) { diff --git a/src/proc_net_snmp.c b/src/proc_net_snmp.c index ba7b40013..7c0fd9b4a 100644 --- a/src/proc_net_snmp.c +++ b/src/proc_net_snmp.c @@ -645,24 +645,35 @@ int do_proc_net_snmp(int update_every, usec_t dt) { if(do_tcp_handshake) { st = rrdset_find_localhost(RRD_TYPE_NET_SNMP ".tcphandshake"); if(!st) { - st = rrdset_create_localhost(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", NULL - , "IPv4 TCP Handshake Issues", "events/s", 2900, update_every - , RRDSET_TYPE_LINE); + 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); + 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); + rrddim_add(st, "TCPSynRetrans", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(st); - rrddim_set(st, "EstabResets", *tcp_EstabResets); - rrddim_set(st, "OutRsts", *tcp_OutRsts); - rrddim_set(st, "ActiveOpens", *tcp_ActiveOpens); - rrddim_set(st, "PassiveOpens", *tcp_PassiveOpens); - rrddim_set(st, "AttemptFails", *tcp_AttemptFails); + rrddim_set(st, "EstabResets", *tcp_EstabResets); + rrddim_set(st, "OutRsts", *tcp_OutRsts); + rrddim_set(st, "ActiveOpens", *tcp_ActiveOpens); + rrddim_set(st, "PassiveOpens", *tcp_PassiveOpens); + rrddim_set(st, "AttemptFails", *tcp_AttemptFails); + rrddim_set(st, "TCPSynRetrans", tcpext_TCPSynRetrans); rrdset_done(st); } } diff --git a/src/proc_net_snmp6.c b/src/proc_net_snmp6.c index 8c4581c1b..aa9ab2209 100644 --- a/src/proc_net_snmp6.c +++ b/src/proc_net_snmp6.c @@ -311,8 +311,8 @@ int do_proc_net_snmp6(int update_every, usec_t dt) { rrddim_set(st, "sent", Ip6OutRequests); rrddim_set(st, "received", Ip6InReceives); - rrddim_set(st, "forwarded", Ip6InDelivers); - rrddim_set(st, "delivers", Ip6OutForwDatagrams); + rrddim_set(st, "forwarded", Ip6OutForwDatagrams); + rrddim_set(st, "delivers", Ip6InDelivers); rrdset_done(st); } diff --git a/src/proc_net_softnet_stat.c b/src/proc_net_softnet_stat.c index 40946a7a5..b03a43c52 100644 --- a/src/proc_net_softnet_stat.c +++ b/src/proc_net_softnet_stat.c @@ -79,7 +79,7 @@ int do_proc_net_softnet_stat(int update_every, usec_t dt) { st = rrdset_find_bytype_localhost("system", "softnet_stat"); if(unlikely(!st)) { - st = rrdset_create_localhost("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat" + st = rrdset_create_localhost("system", "softnet_stat", NULL, "softnet_stat", "system.softnet_stat", "System softnet_stat" , "events/s", 955, update_every, RRDSET_TYPE_LINE); for(w = 0; w < allocated_columns ;w++) if(unlikely(softnet_column_name(w))) @@ -103,7 +103,7 @@ int do_proc_net_softnet_stat(int update_every, usec_t dt) { char title[100+1]; snprintfz(title, 100, "CPU%zu softnet_stat", l); - st = rrdset_create_localhost("cpu", id, NULL, "softnet_stat", NULL, title, "events/s", 4101 + l + st = rrdset_create_localhost("cpu", id, NULL, "softnet_stat", "cpu.softnet_stat", title, "events/s", 4101 + l , update_every, RRDSET_TYPE_LINE); for(w = 0; w < allocated_columns ;w++) if(unlikely(softnet_column_name(w))) diff --git a/src/proc_spl_kstat_zfs.c b/src/proc_spl_kstat_zfs.c new file mode 100644 index 000000000..dee7a6b3d --- /dev/null +++ b/src/proc_spl_kstat_zfs.c @@ -0,0 +1,153 @@ +#include "common.h" +#include "zfs_common.h" + +#define ZFS_PROC_ARCSTATS "/proc/spl/kstat/zfs/arcstats" + +struct arcstats arcstats = { 0 }; + +int do_proc_spl_kstat_zfs_arcstats(int update_every, usec_t dt) { + (void)dt; + + static procfile *ff = NULL; + static ARL_BASE *arl_base = NULL; + + l2exist = -1; + + if(unlikely(!arl_base)) { + arl_base = arl_create("arcstats", NULL, 60); + + arl_expect(arl_base, "hits", &arcstats.hits); + arl_expect(arl_base, "misses", &arcstats.misses); + arl_expect(arl_base, "demand_data_hits", &arcstats.demand_data_hits); + arl_expect(arl_base, "demand_data_misses", &arcstats.demand_data_misses); + arl_expect(arl_base, "demand_metadata_hits", &arcstats.demand_metadata_hits); + arl_expect(arl_base, "demand_metadata_misses", &arcstats.demand_metadata_misses); + arl_expect(arl_base, "prefetch_data_hits", &arcstats.prefetch_data_hits); + arl_expect(arl_base, "prefetch_data_misses", &arcstats.prefetch_data_misses); + arl_expect(arl_base, "prefetch_metadata_hits", &arcstats.prefetch_metadata_hits); + arl_expect(arl_base, "prefetch_metadata_misses", &arcstats.prefetch_metadata_misses); + arl_expect(arl_base, "mru_hits", &arcstats.mru_hits); + arl_expect(arl_base, "mru_ghost_hits", &arcstats.mru_ghost_hits); + arl_expect(arl_base, "mfu_hits", &arcstats.mfu_hits); + arl_expect(arl_base, "mfu_ghost_hits", &arcstats.mfu_ghost_hits); + arl_expect(arl_base, "deleted", &arcstats.deleted); + arl_expect(arl_base, "mutex_miss", &arcstats.mutex_miss); + arl_expect(arl_base, "evict_skip", &arcstats.evict_skip); + arl_expect(arl_base, "evict_not_enough", &arcstats.evict_not_enough); + arl_expect(arl_base, "evict_l2_cached", &arcstats.evict_l2_cached); + arl_expect(arl_base, "evict_l2_eligible", &arcstats.evict_l2_eligible); + arl_expect(arl_base, "evict_l2_ineligible", &arcstats.evict_l2_ineligible); + arl_expect(arl_base, "evict_l2_skip", &arcstats.evict_l2_skip); + arl_expect(arl_base, "hash_elements", &arcstats.hash_elements); + arl_expect(arl_base, "hash_elements_max", &arcstats.hash_elements_max); + arl_expect(arl_base, "hash_collisions", &arcstats.hash_collisions); + arl_expect(arl_base, "hash_chains", &arcstats.hash_chains); + arl_expect(arl_base, "hash_chain_max", &arcstats.hash_chain_max); + arl_expect(arl_base, "p", &arcstats.p); + arl_expect(arl_base, "c", &arcstats.c); + arl_expect(arl_base, "c_min", &arcstats.c_min); + arl_expect(arl_base, "c_max", &arcstats.c_max); + arl_expect(arl_base, "size", &arcstats.size); + arl_expect(arl_base, "hdr_size", &arcstats.hdr_size); + arl_expect(arl_base, "data_size", &arcstats.data_size); + arl_expect(arl_base, "metadata_size", &arcstats.metadata_size); + arl_expect(arl_base, "other_size", &arcstats.other_size); + arl_expect(arl_base, "anon_size", &arcstats.anon_size); + arl_expect(arl_base, "anon_evictable_data", &arcstats.anon_evictable_data); + arl_expect(arl_base, "anon_evictable_metadata", &arcstats.anon_evictable_metadata); + arl_expect(arl_base, "mru_size", &arcstats.mru_size); + arl_expect(arl_base, "mru_evictable_data", &arcstats.mru_evictable_data); + arl_expect(arl_base, "mru_evictable_metadata", &arcstats.mru_evictable_metadata); + arl_expect(arl_base, "mru_ghost_size", &arcstats.mru_ghost_size); + arl_expect(arl_base, "mru_ghost_evictable_data", &arcstats.mru_ghost_evictable_data); + arl_expect(arl_base, "mru_ghost_evictable_metadata", &arcstats.mru_ghost_evictable_metadata); + arl_expect(arl_base, "mfu_size", &arcstats.mfu_size); + arl_expect(arl_base, "mfu_evictable_data", &arcstats.mfu_evictable_data); + arl_expect(arl_base, "mfu_evictable_metadata", &arcstats.mfu_evictable_metadata); + arl_expect(arl_base, "mfu_ghost_size", &arcstats.mfu_ghost_size); + arl_expect(arl_base, "mfu_ghost_evictable_data", &arcstats.mfu_ghost_evictable_data); + arl_expect(arl_base, "mfu_ghost_evictable_metadata", &arcstats.mfu_ghost_evictable_metadata); + arl_expect(arl_base, "l2_hits", &arcstats.l2_hits); + arl_expect(arl_base, "l2_misses", &arcstats.l2_misses); + arl_expect(arl_base, "l2_feeds", &arcstats.l2_feeds); + arl_expect(arl_base, "l2_rw_clash", &arcstats.l2_rw_clash); + arl_expect(arl_base, "l2_read_bytes", &arcstats.l2_read_bytes); + arl_expect(arl_base, "l2_write_bytes", &arcstats.l2_write_bytes); + arl_expect(arl_base, "l2_writes_sent", &arcstats.l2_writes_sent); + arl_expect(arl_base, "l2_writes_done", &arcstats.l2_writes_done); + arl_expect(arl_base, "l2_writes_error", &arcstats.l2_writes_error); + arl_expect(arl_base, "l2_writes_lock_retry", &arcstats.l2_writes_lock_retry); + arl_expect(arl_base, "l2_evict_lock_retry", &arcstats.l2_evict_lock_retry); + arl_expect(arl_base, "l2_evict_reading", &arcstats.l2_evict_reading); + arl_expect(arl_base, "l2_evict_l1cached", &arcstats.l2_evict_l1cached); + arl_expect(arl_base, "l2_free_on_write", &arcstats.l2_free_on_write); + arl_expect(arl_base, "l2_cdata_free_on_write", &arcstats.l2_cdata_free_on_write); + arl_expect(arl_base, "l2_abort_lowmem", &arcstats.l2_abort_lowmem); + arl_expect(arl_base, "l2_cksum_bad", &arcstats.l2_cksum_bad); + arl_expect(arl_base, "l2_io_error", &arcstats.l2_io_error); + arl_expect(arl_base, "l2_size", &arcstats.l2_size); + arl_expect(arl_base, "l2_asize", &arcstats.l2_asize); + arl_expect(arl_base, "l2_hdr_size", &arcstats.l2_hdr_size); + arl_expect(arl_base, "l2_compress_successes", &arcstats.l2_compress_successes); + arl_expect(arl_base, "l2_compress_zeros", &arcstats.l2_compress_zeros); + arl_expect(arl_base, "l2_compress_failures", &arcstats.l2_compress_failures); + arl_expect(arl_base, "memory_throttle_count", &arcstats.memory_throttle_count); + arl_expect(arl_base, "duplicate_buffers", &arcstats.duplicate_buffers); + arl_expect(arl_base, "duplicate_buffers_size", &arcstats.duplicate_buffers_size); + arl_expect(arl_base, "duplicate_reads", &arcstats.duplicate_reads); + arl_expect(arl_base, "memory_direct_count", &arcstats.memory_direct_count); + arl_expect(arl_base, "memory_indirect_count", &arcstats.memory_indirect_count); + arl_expect(arl_base, "arc_no_grow", &arcstats.arc_no_grow); + arl_expect(arl_base, "arc_tempreserve", &arcstats.arc_tempreserve); + arl_expect(arl_base, "arc_loaned_bytes", &arcstats.arc_loaned_bytes); + arl_expect(arl_base, "arc_prune", &arcstats.arc_prune); + arl_expect(arl_base, "arc_meta_used", &arcstats.arc_meta_used); + arl_expect(arl_base, "arc_meta_limit", &arcstats.arc_meta_limit); + arl_expect(arl_base, "arc_meta_max", &arcstats.arc_meta_max); + arl_expect(arl_base, "arc_meta_min", &arcstats.arc_meta_min); + arl_expect(arl_base, "arc_need_free", &arcstats.arc_need_free); + arl_expect(arl_base, "arc_sys_free", &arcstats.arc_sys_free); + } + + if(unlikely(!ff)) { + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, ZFS_PROC_ARCSTATS); + ff = procfile_open(config_get("plugin:proc:" ZFS_PROC_ARCSTATS, "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) + return 1; + } + + ff = procfile_readall(ff); + if(unlikely(!ff)) + return 0; // we return 0, so that we will retry to open it next time + + size_t lines = procfile_lines(ff), l; + + arl_begin(arl_base); + + for(l = 0; l < lines ;l++) { + size_t words = procfile_linewords(ff, l); + if(unlikely(words < 3)) { + if(unlikely(words)) error("Cannot read " ZFS_PROC_ARCSTATS " line %zu. Expected 3 params, read %zu.", l, words); + continue; + } + + const char *key = procfile_lineword(ff, l, 0); + const char *value = procfile_lineword(ff, l, 2); + + if(unlikely(l2exist == -1)) { + if(key[0] == 'l' && key[1] == '2' && key[2] == '_') + l2exist = 1; + } + + if(unlikely(arl_check(arl_base, key, value))) break; + } + + if(unlikely(l2exist == -1)) + l2exist = 0; + + generate_charts_arcstats(update_every); + generate_charts_arc_summary(update_every); + + return 0; +} diff --git a/src/proc_vmstat.c b/src/proc_vmstat.c index 847487363..a2416313a 100644 --- a/src/proc_vmstat.c +++ b/src/proc_vmstat.c @@ -169,11 +169,11 @@ int do_proc_vmstat(int update_every, usec_t dt) { // The following stats depend on CONFIG_NUMA_BALANCING in the // kernel. - 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); + 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); @@ -182,11 +182,11 @@ int do_proc_vmstat(int update_every, usec_t dt) { rrddim_set(st_numa, "interleave", numa_interleave); rrddim_set(st_numa, "other", numa_other); - rrddim_set(st_numa, "pte updates", numa_pte_updates); - rrddim_set(st_numa, "huge pte updates", numa_huge_pte_updates); - rrddim_set(st_numa, "hint faults", numa_hint_faults); - rrddim_set(st_numa, "hint faults local", numa_hint_faults_local); - rrddim_set(st_numa, "pages migrated", numa_pages_migrated); + rrddim_set(st_numa, "pte_updates", numa_pte_updates); + rrddim_set(st_numa, "huge_pte_updates", numa_huge_pte_updates); + rrddim_set(st_numa, "hint_faults", numa_hint_faults); + rrddim_set(st_numa, "hint_faults_local", numa_hint_faults_local); + rrddim_set(st_numa, "pages_migrated", numa_pages_migrated); rrdset_done(st_numa); } diff --git a/src/registry.c b/src/registry.c index ed9be9848..76e3fa4d2 100644 --- a/src/registry.c +++ b/src/registry.c @@ -45,7 +45,7 @@ static inline void registry_json_header(RRDHOST *host, struct web_client *w, con 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, (host == localhost)?registry.hostname:host->hostname, host->machine_guid); + action, status, host->registry_hostname, host->machine_guid); } static inline void registry_json_footer(struct web_client *w) { diff --git a/src/registry.h b/src/registry.h index 2c4592b92..9aa241564 100644 --- a/src/registry.h +++ b/src/registry.h @@ -70,6 +70,8 @@ extern int registry_request_hello_json(RRDHOST *host, struct web_client *w); extern void registry_statistics(void); extern char *registry_get_this_machine_guid(void); +extern char *registry_get_this_machine_hostname(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 2a41d36ee..654f66d12 100644 --- a/src/registry_init.c +++ b/src/registry_init.c @@ -34,7 +34,7 @@ int registry_init(void) { 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.hostname = config_get(CONFIG_SECTION_REGISTRY, "registry hostname", netdata_configured_hostname); registry.verify_cookies_redirects = config_get_boolean(CONFIG_SECTION_REGISTRY, "verify browser cookies support", 1); setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1); diff --git a/src/registry_internals.c b/src/registry_internals.c index 9ec91ba40..fd3c295ce 100644 --- a/src/registry_internals.c +++ b/src/registry_internals.c @@ -274,6 +274,10 @@ static inline int is_machine_guid_blacklisted(const char *guid) { return 0; } +char *registry_get_this_machine_hostname(void) { + return registry.hostname; +} + char *registry_get_this_machine_guid(void) { static char guid[GUID_LEN + 1] = ""; diff --git a/src/rrd.c b/src/rrd.c index a9ff6243d..85ce93dd3 100644 --- a/src/rrd.c +++ b/src/rrd.c @@ -31,19 +31,28 @@ inline const char *rrd_memory_mode_name(RRD_MEMORY_MODE id) { return RRD_MEMORY_MODE_NONE_NAME; case RRD_MEMORY_MODE_SAVE: - default: return RRD_MEMORY_MODE_SAVE_NAME; + + case RRD_MEMORY_MODE_ALLOC: + return RRD_MEMORY_MODE_ALLOC_NAME; } + + return RRD_MEMORY_MODE_SAVE_NAME; } 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; + else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_ALLOC_NAME))) + return RRD_MEMORY_MODE_ALLOC; + return RRD_MEMORY_MODE_SAVE; } diff --git a/src/rrd.h b/src/rrd.h index 2f4f2127f..5bc61dcb8 100644 --- a/src/rrd.h +++ b/src/rrd.h @@ -5,7 +5,7 @@ #define UPDATE_EVERY_MAX 3600 #define RRD_DEFAULT_HISTORY_ENTRIES 3600 -#define RRD_HISTORY_ENTRIES_MAX (86400*10) +#define RRD_HISTORY_ENTRIES_MAX (86400*365) extern int default_rrd_update_every; extern int default_rrd_history_entries; @@ -42,13 +42,15 @@ 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_SAVE = 3, + RRD_MEMORY_MODE_ALLOC = 4 } 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_ALLOC_NAME "alloc" extern RRD_MEMORY_MODE default_rrd_memory_mode; @@ -101,9 +103,15 @@ typedef enum rrddim_flags { RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS = 1 << 1 // do not offer RESET or OVERFLOW info to callers } RRDDIM_FLAGS; +#ifdef HAVE_C___ATOMIC +#define rrddim_flag_check(rd, flag) (__atomic_load_n(&((rd)->flags), __ATOMIC_SEQ_CST) & flag) +#define rrddim_flag_set(rd, flag) __atomic_or_fetch(&((rd)->flags), flag, __ATOMIC_SEQ_CST) +#define rrddim_flag_clear(rd, flag) __atomic_and_fetch(&((rd)->flags), ~flag, __ATOMIC_SEQ_CST) +#else #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 +#endif // ---------------------------------------------------------------------------- @@ -210,16 +218,28 @@ typedef struct rrddim RRDDIM; // 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_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_FLAG_BACKEND_SEND = 1 << 4, // if set, this chart should be sent to backends + RRDSET_FLAG_BACKEND_IGNORE = 1 << 5, // if set, this chart should not be sent to backends + RRDSET_FLAG_EXPOSED_UPSTREAM = 1 << 6, // if set, we have sent this chart to netdata master (streaming) + RRDSET_FLAG_STORE_FIRST = 1 << 7, // if set, do not eliminate the first collection during interpolation + RRDSET_FLAG_HETEROGENEOUS = 1 << 8, // if set, the chart is not homogeneous (dimensions in it have multiple algorithms, multipliers or dividers) + RRDSET_FLAG_HOMEGENEOUS_CHECK= 1 << 9 // if set, the chart should be checked to determine if the dimensions as homogeneous } RRDSET_FLAGS; +#ifdef HAVE_C___ATOMIC +#define rrdset_flag_check(st, flag) (__atomic_load_n(&((st)->flags), __ATOMIC_SEQ_CST) & flag) +#define rrdset_flag_set(st, flag) __atomic_or_fetch(&((st)->flags), flag, __ATOMIC_SEQ_CST) +#define rrdset_flag_clear(st, flag) __atomic_and_fetch(&((st)->flags), ~flag, __ATOMIC_SEQ_CST) +#else #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 +#endif struct rrdset { // ------------------------------------------------------------------------ @@ -279,7 +299,8 @@ struct rrdset { size_t counter_done; // the number of times rrdset_done() has been called time_t last_accessed_time; // the last time this RRDSET has been accessed - size_t unused[9]; + time_t upstream_resync_time; // the timestamp up to which we should resync clock upstream + size_t unused[8]; 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 @@ -347,14 +368,28 @@ typedef struct rrdset RRDSET; // 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_ORPHAN = 1 << 0, // this host is orphan (not receiving data) + RRDHOST_DELETE_OBSOLETE_CHARTS = 1 << 1, // delete files of obsolete charts + RRDHOST_DELETE_ORPHAN_HOST = 1 << 2 // delete the entire host when orphan } RRDHOST_FLAGS; +#ifdef HAVE_C___ATOMIC +#define rrdhost_flag_check(host, flag) (__atomic_load_n(&((host)->flags), __ATOMIC_SEQ_CST) & flag) +#define rrdhost_flag_set(host, flag) __atomic_or_fetch(&((host)->flags), flag, __ATOMIC_SEQ_CST) +#define rrdhost_flag_clear(host, flag) __atomic_and_fetch(&((host)->flags), ~flag, __ATOMIC_SEQ_CST) +#else #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 +#endif + +#ifdef NETDATA_INTERNAL_CHECKS +#define rrdset_debug(st, fmt, args...) do { if(unlikely(debug_flags & D_RRD_STATS && rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) \ + debug_int(__FILE__, __FUNCTION__, __LINE__, "%s: " fmt, st->name, ##args); } while(0) +#else +#define rrdset_debug(st, fmt, args...) debug_dummy() +#endif + // ---------------------------------------------------------------------------- // RRD HOST @@ -368,10 +403,13 @@ struct rrdhost { char *hostname; // the hostname of this host uint32_t hash_hostname; // the hostname hash + char *registry_hostname; // the registry hostname for this host + char machine_guid[GUID_LEN + 1]; // the unique ID of this host uint32_t hash_machine_guid; // the hash of the unique ID - char *os; // the O/S type of the host + const char *os; // the O/S type of the host + const char *tags; // tags for this host uint32_t flags; // flags about this RRDHOST @@ -488,8 +526,10 @@ extern RRDHOST *rrdhost_find_by_guid(const char *guid, uint32_t hash); extern RRDHOST *rrdhost_find_or_create( const char *hostname + , const char *registry_hostname , const char *guid , const char *os + , const char *tags , int update_every , long history , RRD_MEMORY_MODE mode @@ -528,7 +568,7 @@ extern void __rrd_check_wrlock(const char *file, const char *function, const uns extern void rrdset_set_name(RRDSET *st, const char *name); -extern RRDSET *rrdset_create(RRDHOST *host +extern RRDSET *rrdset_create_custom(RRDHOST *host , const char *type , const char *id , const char *name @@ -538,18 +578,27 @@ extern RRDSET *rrdset_create(RRDHOST *host , const char *units , long priority , int update_every - , RRDSET_TYPE chart_type); + , RRDSET_TYPE chart_type + , RRD_MEMORY_MODE memory_mode + , long history_entries); + +#define rrdset_create(host, type, id, name, family, context, title, units, priority, update_every, chart_type) \ + rrdset_create_custom(host, type, id, name, family, context, title, units, priority, update_every, chart_type, (host)->rrd_memory_mode, (host)->rrd_history_entries) -#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) +#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 void rrdhost_free_all(void); extern void rrdhost_save_all(void); +extern void rrdhost_cleanup_all(void); -extern void rrdhost_cleanup_orphan(RRDHOST *protected); +extern void rrdhost_cleanup_orphan_hosts(RRDHOST *protected); extern void rrdhost_free(RRDHOST *host); extern void rrdhost_save(RRDHOST *host); extern void rrdhost_delete(RRDHOST *host); +extern void rrdset_update_heterogeneous_flag(RRDSET *st); + extern RRDSET *rrdset_find(RRDHOST *host, const char *id); #define rrdset_find_localhost(id) rrdset_find(localhost, id) @@ -565,8 +614,12 @@ extern void rrdset_next_usec(RRDSET *st, usec_t microseconds); extern void rrdset_done(RRDSET *st); +extern void rrdset_is_obsolete(RRDSET *st); +extern void rrdset_isnot_obsolete(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) +#define rrdset_is_available_for_viewers(st) (rrdset_flag_check(st, RRDSET_FLAG_ENABLED) && !rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && (st)->dimensions && (st)->rrd_memory_mode != RRD_MEMORY_MODE_NONE) +#define rrdset_is_available_for_backends(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 )) @@ -604,9 +657,14 @@ extern void rrdset_done(RRDSET *st); // ---------------------------------------------------------------------------- // RRD DIMENSION functions -extern RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm); +extern RRDDIM *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm, RRD_MEMORY_MODE memory_mode); +#define rrddim_add(st, id, name, multiplier, divisor, algorithm) rrddim_add_custom(st, id, name, multiplier, divisor, algorithm, (st)->rrd_memory_mode) + +extern int rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name); +extern int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm); +extern int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, collected_number multiplier); +extern int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, collected_number divisor); -extern void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name); extern RRDDIM *rrddim_find(RRDSET *st, const char *id); extern int rrddim_hide(RRDSET *st, const char *id); @@ -647,7 +705,7 @@ 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); +extern void rrdhost_cleanup_obsolete_charts(RRDHOST *host); #endif /* NETDATA_RRD_INTERNALS */ diff --git a/src/rrd2json.c b/src/rrd2json.c index 4d853930c..98080139c 100644 --- a/src/rrd2json.c +++ b/src/rrd2json.c @@ -11,7 +11,7 @@ void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions "\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\"title\": \"%s (%s)\",\n" "\t\t\t\"priority\": %ld,\n" "\t\t\t\"enabled\": %s,\n" "\t\t\t\"units\": \"%s\",\n" @@ -27,7 +27,7 @@ void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions , st->type , st->family , st->context - , st->title + , st->title, st->name , st->priority , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?"true":"false" , st->units @@ -166,79 +166,6 @@ void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb) { buffer_sprintf(wb, "\n\t]\n}\n"); } -// ---------------------------------------------------------------------------- -// PROMETHEUS -// /api/v1/allmetrics?format=prometheus - -static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable) { - size_t n; - - for(n = 0; *s && n < usable ; d++, s++, n++) { - register char c = *s; - - if(unlikely(!isalnum(c))) *d = '_'; - else *d = c; - } - *d = '\0'; - - return n; -} - -#define PROMETHEUS_ELEMENT_MAX 256 - -void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb) { - rrdhost_rdlock(host); - - char hostname[PROMETHEUS_ELEMENT_MAX + 1]; - prometheus_name_copy(hostname, host->hostname, PROMETHEUS_ELEMENT_MAX); - - // for each chart - RRDSET *st; - 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(rrdset_is_available_for_viewers(st)) { - rrdset_rdlock(st); - - // for each dimension - RRDDIM *rd; - 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 RRD_ALGORITHM_INCREMENTAL: - case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL: - buffer_sprintf(wb, "# TYPE %s_%s counter\n", chart, dimension); - break; - - default: - buffer_sprintf(wb, "# TYPE %s_%s gauge\n", chart, dimension); - break; - } - - // 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, timeval_msec(&rd->last_collected_time)); - - buffer_sprintf(wb, "%s_%s{instance=\"%s\"} " COLLECTED_NUMBER_FORMAT " %llu\n", - chart, dimension, hostname, rd->last_collected_value, timeval_msec(&rd->last_collected_time) - ); - - } - } - - rrdset_unlock(st); - } - } - - rrdhost_unlock(host); -} - // ---------------------------------------------------------------------------- // BASH // /api/v1/allmetrics?format=bash @@ -267,7 +194,7 @@ void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) { 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); + shell_name_copy(chart, st->name?st->name:st->id, SHELL_ELEMENT_MAX); buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", st->id, st->name); if(rrdset_is_available_for_viewers(st)) { @@ -278,7 +205,7 @@ void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) { rrddim_foreach_read(rd, st) { if(rd->collections_counter) { char dimension[SHELL_ELEMENT_MAX + 1]; - shell_name_copy(dimension, rd->id, SHELL_ELEMENT_MAX); + shell_name_copy(dimension, rd->name?rd->name:rd->id, SHELL_ELEMENT_MAX); calculated_number n = rd->last_stored_value; @@ -306,7 +233,7 @@ void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) { if(!rc->rrdset) continue; char chart[SHELL_ELEMENT_MAX + 1]; - shell_name_copy(chart, rc->rrdset->id, SHELL_ELEMENT_MAX); + shell_name_copy(chart, rc->rrdset->name?rc->rrdset->name:rc->rrdset->id, SHELL_ELEMENT_MAX); char alarm[SHELL_ELEMENT_MAX + 1]; shell_name_copy(alarm, rc->name, SHELL_ELEMENT_MAX); diff --git a/src/rrd2json.h b/src/rrd2json.h index f2f03c640..a3b9c9509 100644 --- a/src/rrd2json.h +++ b/src/rrd2json.h @@ -31,13 +31,15 @@ #define DATASOURCE_FORMAT_SSV_COMMA "ssvcomma" #define DATASOURCE_FORMAT_CSV_JSON_ARRAY "csvjsonarray" -#define ALLMETRICS_FORMAT_SHELL "shell" -#define ALLMETRICS_FORMAT_PROMETHEUS "prometheus" -#define ALLMETRICS_FORMAT_JSON "json" +#define ALLMETRICS_FORMAT_SHELL "shell" +#define ALLMETRICS_FORMAT_PROMETHEUS "prometheus" +#define ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS "prometheus_all_hosts" +#define ALLMETRICS_FORMAT_JSON "json" -#define ALLMETRICS_SHELL 1 -#define ALLMETRICS_PROMETHEUS 2 -#define ALLMETRICS_JSON 3 +#define ALLMETRICS_SHELL 1 +#define ALLMETRICS_PROMETHEUS 2 +#define ALLMETRICS_JSON 3 +#define ALLMETRICS_PROMETHEUS_ALL_HOSTS 4 #define GROUP_UNDEFINED 0 #define GROUP_AVERAGE 1 @@ -65,13 +67,13 @@ 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); + , 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); + , 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 index 6710f31cf..003b8626d 100644 --- a/src/rrd2json_api_old.c +++ b/src/rrd2json_api_old.c @@ -14,7 +14,7 @@ unsigned long rrdset_info2json_api_old(RRDSET *st, char *options, BUFFER *wb) { "\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\"title\": \"%s (%s)\",\n" "\t\t\t\"priority\": %ld,\n" "\t\t\t\"enabled\": %d,\n" "\t\t\t\"units\": \"%s\",\n" @@ -37,7 +37,7 @@ unsigned long rrdset_info2json_api_old(RRDSET *st, char *options, BUFFER *wb) { , st->type , st->family , st->context - , st->title + , st->title, st->name , st->priority , rrdset_flag_check(st, RRDSET_FLAG_ENABLED)?1:0 , st->units diff --git a/src/rrdcalc.c b/src/rrdcalc.c index 1f1845409..bb90a4c6d 100644 --- a/src/rrdcalc.c +++ b/src/rrdcalc.c @@ -219,7 +219,7 @@ inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, ui 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); + info("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname); return 1; } } diff --git a/src/rrdcalctemplate.c b/src/rrdcalctemplate.c index 2c5e2bd16..b5d2c7d61 100644 --- a/src/rrdcalctemplate.c +++ b/src/rrdcalctemplate.c @@ -12,7 +12,7 @@ void rrdcalctemplate_link_matching(RRDSET *st) { && (!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); + info("Health tried to create alarm from template '%s' on chart '%s' of host '%s', but it failed", rt->name, st->id, st->rrdhost->hostname); #ifdef NETDATA_INTERNAL_CHECKS else if(rc->rrdset != st) diff --git a/src/rrddim.c b/src/rrddim.c index 54a17522f..e75aa3fd2 100644 --- a/src/rrddim.c +++ b/src/rrddim.c @@ -35,9 +35,9 @@ inline RRDDIM *rrddim_find(RRDSET *st, const char *id) { // ---------------------------------------------------------------------------- // RRDDIM rename a dimension -inline void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name) { - if(unlikely(!strcmp(rd->name, name))) - return; +inline int rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name) { + if(unlikely(!name || !*name || !strcmp(rd->name, name))) + return 0; debug(D_RRD_CALLS, "rrddim_set_name() from %s.%s to %s.%s", st->name, rd->name, st->name, name); @@ -45,18 +45,57 @@ inline void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name) { 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); + rd->exposed = 0; + return 1; +} + +inline int rrddim_set_algorithm(RRDSET *st, RRDDIM *rd, RRD_ALGORITHM algorithm) { + if(unlikely(rd->algorithm == algorithm)) + return 0; + + debug(D_RRD_CALLS, "Updating algorithm of dimension '%s/%s' from %s to %s", st->id, rd->name, rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(algorithm)); + rd->algorithm = algorithm; + rd->exposed = 0; + rrdset_flag_set(st, RRDSET_FLAG_HOMEGENEOUS_CHECK); + return 1; +} + +inline int rrddim_set_multiplier(RRDSET *st, RRDDIM *rd, collected_number multiplier) { + if(unlikely(rd->multiplier == multiplier)) + return 0; + + debug(D_RRD_CALLS, "Updating multiplier of dimension '%s/%s' from " COLLECTED_NUMBER_FORMAT " to " COLLECTED_NUMBER_FORMAT, st->id, rd->name, rd->multiplier, multiplier); + rd->multiplier = multiplier; + rd->exposed = 0; + rrdset_flag_set(st, RRDSET_FLAG_HOMEGENEOUS_CHECK); + return 1; } +inline int rrddim_set_divisor(RRDSET *st, RRDDIM *rd, collected_number divisor) { + if(unlikely(rd->divisor == divisor)) + return 0; + + debug(D_RRD_CALLS, "Updating divisor of dimension '%s/%s' from " COLLECTED_NUMBER_FORMAT " to " COLLECTED_NUMBER_FORMAT, st->id, rd->name, rd->divisor, divisor); + rd->divisor = divisor; + rd->exposed = 0; + rrdset_flag_set(st, RRDSET_FLAG_HOMEGENEOUS_CHECK); + return 1; +} // ---------------------------------------------------------------------------- // 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 *rrddim_add_custom(RRDSET *st, const char *id, const char *name, collected_number multiplier, collected_number divisor, RRD_ALGORITHM algorithm, RRD_MEMORY_MODE memory_mode) { 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:""); + + rrddim_set_name(st, rd, name); + rrddim_set_algorithm(st, rd, algorithm); + rrddim_set_multiplier(st, rd, multiplier); + rrddim_set_divisor(st, rd, divisor); + return rd; } @@ -71,8 +110,14 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe 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(memory_mode == RRD_MEMORY_MODE_SAVE || memory_mode == RRD_MEMORY_MODE_MAP || memory_mode == RRD_MEMORY_MODE_RAM) { + rd = (RRDDIM *)mymmap( + (memory_mode == RRD_MEMORY_MODE_RAM)?NULL:fullfilename + , size + , ((memory_mode == RRD_MEMORY_MODE_MAP) ? MAP_SHARED : MAP_PRIVATE) + , 1 + ); + if(likely(rd)) { // we have a file mapped for rd @@ -83,56 +128,64 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe rd->variables = NULL; rd->next = NULL; rd->rrdset = NULL; + rd->exposed = 0; struct timeval now; now_realtime_timeval(&now); - if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) { - errno = 0; - info("Initializing file %s.", fullfilename); + if(memory_mode == RRD_MEMORY_MODE_RAM) { 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); + else { + int reset = 0; + + if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) { + info("Initializing file %s.", fullfilename); + memset(rd, 0, size); + reset = 1; + } + else if(rd->memsize != size) { + error("File %s does not have the desired size, expected %lu but found %lu. Clearing it.", fullfilename, size, rd->memsize); + memset(rd, 0, size); + reset = 1; + } + else if(rd->update_every != st->update_every) { + error("File %s does not have the same update frequency, expected %d but found %d. Clearing it.", fullfilename, st->update_every, rd->update_every); + memset(rd, 0, size); + reset = 1; + } + else if(dt_usec(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * USEC_PER_SEC)) { + error("File %s is too old (last collected %llu seconds ago, but the database is %ld seconds). Clearing it.", fullfilename, dt_usec(&now, &rd->last_collected_time) / USEC_PER_SEC, rd->entries * rd->update_every); + memset(rd, 0, size); + reset = 1; + } + + if(!reset) { + if(rd->algorithm != algorithm) { + info("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)); + } + + if(rd->multiplier != multiplier) { + info("File %s does not have the expected multiplier (expected " COLLECTED_NUMBER_FORMAT ", found " COLLECTED_NUMBER_FORMAT ". Previous values may be wrong.", fullfilename, multiplier, rd->multiplier); + } + + if(rd->divisor != divisor) { + info("File %s does not have the expected divisor (expected " COLLECTED_NUMBER_FORMAT ", found " COLLECTED_NUMBER_FORMAT ". Previous values may be wrong.", fullfilename, divisor, rd->divisor); + } + } } - 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; + rd->rrd_memory_mode = 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->rrd_memory_mode = (memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_ALLOC; } rd->memsize = size; @@ -161,8 +214,11 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe rd->entries = st->entries; rd->update_every = st->update_every; - // prevent incremental calculation spikes - rd->collections_counter = 0; + if(rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST)) + rd->collections_counter = 1; + else + rd->collections_counter = 0; + rd->updated = 0; rd->flags = 0x00000000; @@ -173,7 +229,7 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe 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->values[st->current_entry] = SN_EMPTY_SLOT; // pack_storage_number(0, SN_NOT_EXISTS); rd->last_collected_time.tv_sec = 0; rd->last_collected_time.tv_usec = 0; rd->rrdset = st; @@ -184,6 +240,23 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe st->dimensions = rd; else { RRDDIM *td = st->dimensions; + + if(td->algorithm != rd->algorithm || abs(td->multiplier) != abs(rd->multiplier) || abs(td->divisor) != abs(rd->divisor)) { + if(!rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) { + #ifdef NETDATA_INTERNAL_CHECKS + info("Dimension '%s' added on chart '%s' of host '%s' is not homogeneous to other dimensions already present (algorithm is '%s' vs '%s', multiplier is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ", divisor is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ").", + rd->name, + st->name, + st->rrdhost->hostname, + rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(td->algorithm), + rd->multiplier, td->multiplier, + rd->divisor, td->divisor + ); + #endif + rrdset_flag_set(st, RRDSET_FLAG_HETEROGENEOUS); + } + } + for(; td->next; td = td->next) ; td->next = rd; } @@ -202,7 +275,6 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe return(rd); } - // ---------------------------------------------------------------------------- // RRDDIM remove / free a dimension @@ -233,19 +305,16 @@ void rrddim_free(RRDSET *st, RRDDIM *rd) 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: + case RRD_MEMORY_MODE_RAM: 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_ALLOC: 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); @@ -263,7 +332,7 @@ int rrddim_hide(RRDSET *st, const char *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); + error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, st->name, st->id, st->rrdhost->hostname); return 1; } @@ -276,7 +345,7 @@ int rrddim_unhide(RRDSET *st, const char *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); + error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, st->name, st->id, st->rrdhost->hostname); return 1; } @@ -305,7 +374,7 @@ inline collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_ 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); + error("Cannot find dimension with id '%s' on stats '%s' (%s) on host '%s'.", id, st->name, st->id, st->rrdhost->hostname); return 0; } diff --git a/src/rrdhost.c b/src/rrdhost.c index a2310330d..ff8aa5617 100644 --- a/src/rrdhost.c +++ b/src/rrdhost.c @@ -58,15 +58,32 @@ RRDHOST *rrdhost_find_by_hostname(const char *hostname, uint32_t hash) { // ---------------------------------------------------------------------------- // RRDHOST - internal helpers +static inline void rrdhost_init_tags(RRDHOST *host, const char *tags) { + if(host->tags && tags && !strcmp(host->tags, tags)) + return; + + void *old = (void *)host->tags; + host->tags = (tags && *tags)?strdupz(tags):NULL; + freez(old); +} + static inline void rrdhost_init_hostname(RRDHOST *host, const char *hostname) { - freez(host->hostname); - host->hostname = strdupz(hostname); + if(host->hostname && hostname && !strcmp(host->hostname, hostname)) + return; + + void *old = host->hostname; + host->hostname = strdupz(hostname?hostname:"localhost"); host->hash_hostname = simple_hash(host->hostname); + freez(old); } static inline void rrdhost_init_os(RRDHOST *host, const char *os) { - freez(host->os); + if(host->os && os && !strcmp(host->os, os)) + return; + + void *old = (void *)host->os; host->os = strdupz(os?os:"unknown"); + freez(old); } static inline void rrdhost_init_machine_guid(RRDHOST *host, const char *machine_guid) { @@ -80,16 +97,18 @@ static inline void rrdhost_init_machine_guid(RRDHOST *host, const char *machine_ // 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 + const char *registry_hostname, + const char *guid, + const char *os, + const char *tags, + 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); @@ -115,6 +134,8 @@ RRDHOST *rrdhost_create(const char *hostname, rrdhost_init_hostname(host, hostname); rrdhost_init_machine_guid(host, guid); rrdhost_init_os(host, os); + rrdhost_init_tags(host, tags); + host->registry_hostname = strdupz((registry_hostname && *registry_hostname)?registry_hostname:hostname); avl_init_lock(&(host->rrdset_root_index), rrdset_compare); avl_init_lock(&(host->rrdset_root_index_name), rrdset_compare_name); @@ -122,10 +143,10 @@ RRDHOST *rrdhost_create(const char *hostname, 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); + rrdhost_flag_set(host, RRDHOST_DELETE_OBSOLETE_CHARTS); if(config_get_boolean(CONFIG_SECTION_GLOBAL, "delete orphan hosts files", 1) && !is_localhost) - rrdhost_flag_set(host, RRDHOST_DELETE_ORPHAN_FILES); + rrdhost_flag_set(host, RRDHOST_DELETE_ORPHAN_HOST); // ------------------------------------------------------------------------ @@ -229,8 +250,9 @@ RRDHOST *rrdhost_create(const char *hostname, host = NULL; } else { - info("Host '%s' with guid '%s' initialized" + info("Host '%s' (at registry as '%s') with guid '%s' initialized" ", os %s" + ", tags '%s'" ", update every %d" ", memory mode %s" ", history entries %ld" @@ -243,8 +265,10 @@ RRDHOST *rrdhost_create(const char *hostname, ", alarms default handler '%s'" ", alarms default recipient '%s'" , host->hostname + , host->registry_hostname , host->machine_guid , host->os + , (host->tags)?host->tags:"" , host->rrd_update_every , rrd_memory_mode_name(host->rrd_memory_mode) , host->rrd_history_entries @@ -267,8 +291,10 @@ RRDHOST *rrdhost_create(const char *hostname, RRDHOST *rrdhost_find_or_create( const char *hostname + , const char *registry_hostname , const char *guid , const char *os + , const char *tags , int update_every , long history , RRD_MEMORY_MODE mode @@ -284,8 +310,10 @@ RRDHOST *rrdhost_find_or_create( if(!host) { host = rrdhost_create( hostname + , registry_hostname , guid , os + , tags , update_every , history , mode @@ -307,22 +335,25 @@ RRDHOST *rrdhost_find_or_create( } 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); + error("Host '%s' has an update frequency of %d seconds, but the wanted one is %d seconds. Restart netdata here to apply the new settings.", 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_history_entries < history) + error("Host '%s' has history of %ld entries, but the wanted one is %ld entries. Restart netdata here to apply the new settings.", 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)); + error("Host '%s' has memory mode '%s', but the wanted one is '%s'. Restart netdata here to apply the new settings.", host->hostname, rrd_memory_mode_name(host->rrd_memory_mode), rrd_memory_mode_name(mode)); + + // update host tags + rrdhost_init_tags(host, tags); } rrd_unlock(); - rrdhost_cleanup_orphan(host); + rrdhost_cleanup_orphan_hosts(host); return host; } -static inline int rrdhost_should_be_deleted(RRDHOST *host, RRDHOST *protected, time_t now) { +static inline int rrdhost_should_be_removed(RRDHOST *host, RRDHOST *protected, time_t now) { if(host != protected && host != localhost && !host->connected_senders @@ -333,7 +364,7 @@ static inline int rrdhost_should_be_deleted(RRDHOST *host, RRDHOST *protected, t return 0; } -void rrdhost_cleanup_orphan(RRDHOST *protected) { +void rrdhost_cleanup_orphan_hosts(RRDHOST *protected) { time_t now = now_realtime_sec(); rrd_wrlock(); @@ -342,10 +373,10 @@ void rrdhost_cleanup_orphan(RRDHOST *protected) { restart_after_removal: rrdhost_foreach_write(host) { - if(rrdhost_should_be_deleted(host, protected, now)) { + if(rrdhost_should_be_removed(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)) + if(rrdset_flag_check(host, RRDHOST_DELETE_ORPHAN_HOST) && rrdset_flag_check(host, RRDHOST_ORPHAN)) rrdhost_delete(host); else rrdhost_save(host); @@ -372,8 +403,10 @@ void rrd_init(char *hostname) { rrd_wrlock(); localhost = rrdhost_create( hostname + , registry_get_this_machine_hostname() , registry_get_this_machine_guid() , os_type + , config_get(CONFIG_SECTION_BACKEND, "host tags", "") , default_rrd_update_every , default_rrd_history_entries , default_rrd_memory_mode @@ -473,7 +506,8 @@ void rrdhost_free(RRDHOST *host) { // ------------------------------------------------------------------------ // free it - freez(host->os); + freez((void *)host->tags); + freez((void *)host->os); freez(host->cache_dir); freez(host->varlib_dir); freez(host->rrdpush_api_key); @@ -482,6 +516,7 @@ void rrdhost_free(RRDHOST *host) { freez(host->health_default_recipient); freez(host->health_log_filename); freez(host->hostname); + freez(host->registry_hostname); rrdhost_unlock(host); netdata_rwlock_destroy(&host->health_log.alarm_log_rwlock); netdata_rwlock_destroy(&host->rrdhost_rwlock); @@ -497,12 +532,12 @@ void rrdhost_free_all(void) { } // ---------------------------------------------------------------------------- -// RRDHOST - save +// RRDHOST - save host files void rrdhost_save(RRDHOST *host) { if(!host) return; - info("Saving database of host '%s'...", host->hostname); + info("Saving/Closing database of host '%s'...", host->hostname); RRDSET *st; @@ -520,7 +555,7 @@ void rrdhost_save(RRDHOST *host) { } // ---------------------------------------------------------------------------- -// RRDHOST - delete files +// RRDHOST - delete host files void rrdhost_delete(RRDHOST *host) { if(!host) return; @@ -539,9 +574,43 @@ void rrdhost_delete(RRDHOST *host) { rrdset_unlock(st); } + recursively_delete_dir(host->cache_dir, "left over host"); + rrdhost_unlock(host); } +// ---------------------------------------------------------------------------- +// RRDHOST - cleanup host files + +void rrdhost_cleanup(RRDHOST *host) { + if(!host) return; + + info("Cleaning up 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); + + if(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE) && rrdhost_flag_check(host, RRDHOST_DELETE_OBSOLETE_CHARTS)) + rrdset_delete(st); + else + rrdset_save(st); + + rrdset_unlock(st); + } + + rrdhost_unlock(host); +} + + +// ---------------------------------------------------------------------------- +// RRDHOST - save all hosts to disk + void rrdhost_save_all(void) { info("Saving database [%zu hosts(s)]...", rrd_hosts_available); @@ -554,7 +623,30 @@ void rrdhost_save_all(void) { rrd_unlock(); } -void rrdhost_cleanup_obsolete(RRDHOST *host) { +// ---------------------------------------------------------------------------- +// RRDHOST - save or delete all hosts from disk + +void rrdhost_cleanup_all(void) { + info("Cleaning up database [%zu hosts(s)]...", rrd_hosts_available); + + rrd_rdlock(); + + RRDHOST *host; + rrdhost_foreach_read(host) { + if(host != localhost && rrdhost_flag_check(host, RRDHOST_DELETE_OBSOLETE_CHARTS) && !host->connected_senders) + rrdhost_delete(host); + else + rrdhost_cleanup(host); + } + + rrd_unlock(); +} + + +// ---------------------------------------------------------------------------- +// RRDHOST - save or delete all the host charts from disk + +void rrdhost_cleanup_obsolete_charts(RRDHOST *host) { time_t now = now_realtime_sec(); RRDSET *st; @@ -569,7 +661,7 @@ restart_after_removal: rrdset_rdlock(st); - if(rrdhost_flag_check(host, RRDHOST_DELETE_OBSOLETE_FILES)) + if(rrdhost_flag_check(host, RRDHOST_DELETE_OBSOLETE_CHARTS)) rrdset_delete(st); else rrdset_save(st); diff --git a/src/rrdpush.c b/src/rrdpush.c index 72e6d8a73..6def90fe5 100644 --- a/src/rrdpush.c +++ b/src/rrdpush.c @@ -57,13 +57,16 @@ int rrdpush_init() { // 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; +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) { + if(unlikely(!(rrdset_flag_check(st, RRDSET_FLAG_EXPOSED_UPSTREAM)))) + return 1; + RRDDIM *rd; rrddim_foreach_read(rd, st) if(!rd->exposed) @@ -74,7 +77,9 @@ static inline int need_to_send_chart_definition(RRDSET *st) { // 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" + rrdset_flag_set(st, RRDSET_FLAG_EXPOSED_UPSTREAM); + + buffer_sprintf(st->rrdhost->rrdpush_buffer, "CHART \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" %ld %d \"%s %s %s\"\n" , st->id , st->name , st->title @@ -84,11 +89,14 @@ static inline void send_chart_definition(RRDSET *st) { , rrdset_type_name(st->chart_type) , st->priority , st->update_every + , rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)?"obsolete":"" + , rrdset_flag_check(st, RRDSET_FLAG_DETAIL)?"detail":"" + , rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST)?"store_first":"" ); 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" + 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) @@ -99,11 +107,13 @@ static inline void send_chart_definition(RRDSET *st) { ); rd->exposed = 1; } + + st->upstream_resync_time = st->last_collected_time.tv_sec + (remote_clock_resync_iterations * st->update_every); } // 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); + buffer_sprintf(st->rrdhost->rrdpush_buffer, "BEGIN %s %llu\n", st->id, (st->upstream_resync_time > st->last_collected_time.tv_sec)?st->usec_since_last_update:0); RRDDIM *rd; rrddim_foreach_read(rd, st) { @@ -117,7 +127,17 @@ static inline void send_chart_metrics(RRDSET *st) { buffer_strcat(st->rrdhost->rrdpush_buffer, "END\n"); } -void rrdpush_sender_thread_spawn(RRDHOST *host); +static void rrdpush_sender_thread_spawn(RRDHOST *host); + +void rrdset_push_chart_definition(RRDSET *st) { + RRDHOST *host = st->rrdhost; + + rrdset_rdlock(st); + rrdpush_lock(host); + send_chart_definition(st); + rrdpush_unlock(host); + rrdset_unlock(st); +} void rrdset_done_push(RRDSET *st) { RRDHOST *host = st->rrdhost; @@ -167,9 +187,7 @@ static void rrdpush_sender_thread_reset_all_charts(RRDHOST *host) { RRDSET *st; rrdset_foreach_read(st, host) { - // make it re-align the current time - // on the remote host - st->counter_done = 0; + st->upstream_resync_time = 0; rrdset_rdlock(st); @@ -219,8 +237,6 @@ static void rrdpush_sender_thread_cleanup_locked_all(RRDHOST *host) { host->rrdpush_buffer = NULL; host->rrdpush_spawn = 0; - - rrdhost_flag_set(host, RRDHOST_ORPHAN); } void rrdpush_sender_thread_stop(RRDHOST *host) { @@ -274,6 +290,7 @@ void *rrdpush_sender_thread(void *ptr) { .tv_usec = 0 }; + time_t last_sent_t = 0; struct pollfd fds[2], *ifd, *ofd; nfds_t fdmax; @@ -281,8 +298,16 @@ void *rrdpush_sender_thread(void *ptr) { ofd = &fds[1]; for(; host->rrdpush_enabled && !netdata_exit ;) { + debug(D_STREAM, "STREAM: Checking if we need to timeout the connection..."); + if(host->rrdpush_socket != -1 && now_monotonic_sec() - last_sent_t > timeout) { + error("STREAM %s [send to %s]: could not send metrics for %d seconds - closing connection - we have sent %zu bytes on this connection.", host->hostname, connected_to, timeout, sent_connection); + close(host->rrdpush_socket); + host->rrdpush_socket = -1; + } if(unlikely(host->rrdpush_socket == -1)) { + debug(D_STREAM, "STREAM: Attempting to connect..."); + // stop appending data into rrdpush_buffer // they will be lost, so there is no point to do it host->rrdpush_connected = 0; @@ -298,16 +323,19 @@ void *rrdpush_sender_thread(void *ptr) { 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" + #define HTTP_HEADER_SIZE 8192 + char http[HTTP_HEADER_SIZE + 1]; + snprintfz(http, HTTP_HEADER_SIZE, + "STREAM key=%s&hostname=%s®istry_hostname=%s&machine_guid=%s&update_every=%d&os=%s&tags=%s 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->registry_hostname , host->machine_guid - , host->os , default_rrd_update_every + , host->os + , (host->tags)?host->tags:"" , program_version ); @@ -321,7 +349,7 @@ void *rrdpush_sender_thread(void *ptr) { 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) { + if(recv_timeout(host->rrdpush_socket, http, HTTP_HEADER_SIZE, 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); @@ -338,15 +366,21 @@ void *rrdpush_sender_thread(void *ptr) { } info("STREAM %s [send to %s]: established communication - sending metrics...", host->hostname, connected_to); + last_sent_t = now_monotonic_sec(); - if(fcntl(host->rrdpush_socket, F_SETFL, O_NONBLOCK) < 0) + if(sock_setnonblock(host->rrdpush_socket) < 0) error("STREAM %s [send to %s]: cannot set non-blocking mode for socket.", host->hostname, connected_to); + if(sock_enlarge_out(host->rrdpush_socket) < 0) + error("STREAM %s [send to %s]: cannot enlarge the socket buffer.", host->hostname, connected_to); + rrdpush_sender_thread_data_flush(host); sent_connection = 0; // allow appending data into rrdpush_buffer host->rrdpush_connected = 1; + + debug(D_STREAM, "Connected..."); } ifd->fd = host->rrdpush_pipe[PIPE_READ]; @@ -356,82 +390,113 @@ void *rrdpush_sender_thread(void *ptr) { ofd->fd = host->rrdpush_socket; ofd->revents = 0; if(begin < buffer_strlen(host->rrdpush_buffer)) { + debug(D_STREAM, "STREAM: Requesting data output on streaming socket..."); ofd->events = POLLOUT; fdmax = 2; } else { + debug(D_STREAM, "STREAM: Not requesting data output on streaming socket (nothing to send now)..."); ofd->events = 0; fdmax = 1; } + debug(D_STREAM, "STREAM: Waiting for poll() events (current buffer length %zu bytes)...", buffer_strlen(host->rrdpush_buffer)); if(netdata_exit) break; - int retval = poll(fds, fdmax, timeout * 1000); + int retval = poll(fds, fdmax, 1000); if(netdata_exit) break; if(unlikely(retval == -1)) { - if(errno == EAGAIN || errno == EINTR) + debug(D_STREAM, "STREAM: poll() failed (current buffer length %zu bytes)...", buffer_strlen(host->rrdpush_buffer)); + + if(errno == EAGAIN || errno == EINTR) { + debug(D_STREAM, "STREAM: poll() failed with EAGAIN or 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)) { + else if(likely(retval)) { + if (ifd->revents & POLLIN) { + debug(D_STREAM, "STREAM: Data added to send buffer (current buffer length %zu bytes)...", 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); + 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); + } - 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); + if (ofd->revents & POLLOUT && begin < buffer_strlen(host->rrdpush_buffer)) { + debug(D_STREAM, "STREAM: Sending data (current buffer length %zu bytes)...", 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); + + debug(D_STREAM, "STREAM: Getting exclusive lock on host..."); + rrdpush_lock(host); + + debug(D_STREAM, "STREAM: Sending data, starting from %zu, size %zu...", begin, buffer_strlen(host->rrdpush_buffer)); + ssize_t ret = send(host->rrdpush_socket, &host->rrdpush_buffer->buffer[begin], buffer_strlen(host->rrdpush_buffer) - begin, MSG_DONTWAIT); + if (unlikely(ret == -1)) { + if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) { + debug(D_STREAM, "STREAM: Send failed - closing socket..."); + 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 { + debug(D_STREAM, "STREAM: Send failed - will retry..."); + } + } + else if(likely(ret > 0)) { + sent_connection += ret; + sent_bytes += ret; + begin += ret; + + if (begin == buffer_strlen(host->rrdpush_buffer)) { + // we send it all + + debug(D_STREAM, "STREAM: Sent %zd bytes (the whole buffer)...", ret); + buffer_flush(host->rrdpush_buffer); + begin = 0; + } + else { + debug(D_STREAM, "STREAM: Sent %zd bytes (part of the data buffer)...", ret); + } + + last_sent_t = now_monotonic_sec(); + } + else { + debug(D_STREAM, "STREAM: send() returned %zd - closing the socket...", ret); + error("STREAM %s [send to %s]: failed to send metrics (send() returned %zd) - closing connection - we have sent %zu bytes on this connection.", host->hostname, connected_to, ret, 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); + debug(D_STREAM, "STREAM: Releasing exclusive lock on 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); + 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 + // END RRDPUSH LOCKED SESSION + } + } + else { + debug(D_STREAM, "STREAM: poll() timed out."); } // protection from overflow - if(host->rrdpush_buffer->len > max_size) { + if(buffer_strlen(host->rrdpush_buffer) > max_size) { + debug(D_STREAM, "STREAM: Buffer is too big (%zu bytes), bigger than the max (%zu) - flushing it...", buffer_strlen(host->rrdpush_buffer), 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) { @@ -464,7 +529,7 @@ cleanup: // ---------------------------------------------------------------------------- // 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) { +static int rrdpush_receive(int fd, const char *key, const char *hostname, const char *registry_hostname, const char *machine_guid, const char *os, const char *tags, 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; @@ -499,13 +564,18 @@ int rrdpush_receive(int fd, const char *key, const char *hostname, const char *m 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); + tags = appconfig_set_default(&stream_config, machine_guid, "host tags", (tags)?tags:""); + if(tags && !*tags) tags = NULL; + if(!strcmp(machine_guid, "localhost")) host = localhost; else host = rrdhost_find_or_create( hostname + , registry_hostname , machine_guid , os + , tags , update_every , history , mode @@ -522,7 +592,7 @@ int rrdpush_receive(int fd, const char *key, const char *hostname, const char *m } #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" + 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, tags '%s'" , hostname , client_ip , client_port @@ -532,6 +602,7 @@ int rrdpush_receive(int fd, const char *key, const char *hostname, const char *m , 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") + , host->tags ); #endif // NETDATA_INTERNAL_CHECKS @@ -560,7 +631,7 @@ int rrdpush_receive(int fd, const char *key, const char *hostname, const char *m } // remove the non-blocking flag from the socket - if(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK) == -1) + if(sock_delnonblock(fd) < 0) 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 * @@ -572,7 +643,11 @@ int rrdpush_receive(int fd, const char *key, const char *hostname, const char *m } rrdhost_wrlock(host); + if(host->connected_senders > 0) + info("STREAM %s [receive from [%s]:%s]: multiple streaming connections for the same host detected. If multiple netdata are pushing metrics for the same charts, at the same time, the result is unexpected.", host->hostname, client_ip, client_port); + host->connected_senders++; + rrdhost_flag_clear(host, RRDHOST_ORPHAN); if(health_enabled != CONFIG_BOOLEAN_NO) host->health_delay_up_to = now_realtime_sec() + alarms_delay; rrdhost_unlock(host); @@ -586,6 +661,7 @@ int rrdpush_receive(int fd, const char *key, const char *hostname, const char *m host->senders_disconnected_time = now_realtime_sec(); host->connected_senders--; if(!host->connected_senders) { + rrdhost_flag_set(host, RRDHOST_ORPHAN); if(health_enabled == CONFIG_BOOLEAN_AUTO) host->health_enabled = 0; } @@ -603,14 +679,16 @@ struct rrdpush_thread { int fd; char *key; char *hostname; + char *registry_hostname; char *machine_guid; char *os; + char *tags; char *client_ip; char *client_port; int update_every; }; -void *rrdpush_receiver_thread(void *ptr) { +static void *rrdpush_receiver_thread(void *ptr) { struct rrdpush_thread *rpt = (struct rrdpush_thread *)ptr; if (pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) @@ -621,13 +699,15 @@ void *rrdpush_receiver_thread(void *ptr) { 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); + rrdpush_receive(rpt->fd, rpt->key, rpt->hostname, rpt->registry_hostname, rpt->machine_guid, rpt->os, rpt->tags, 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->registry_hostname); freez(rpt->machine_guid); freez(rpt->os); + freez(rpt->tags); freez(rpt->client_ip); freez(rpt->client_port); freez(rpt); @@ -636,7 +716,7 @@ void *rrdpush_receiver_thread(void *ptr) { return NULL; } -void rrdpush_sender_thread_spawn(RRDHOST *host) { +static void rrdpush_sender_thread_spawn(RRDHOST *host) { rrdhost_wrlock(host); if(!host->rrdpush_spawn) { @@ -646,7 +726,6 @@ void rrdpush_sender_thread_spawn(RRDHOST *host) { 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; } @@ -658,7 +737,7 @@ int rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url 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"; + char *key = NULL, *hostname = NULL, *registry_hostname = NULL, *machine_guid = NULL, *os = "unknown", *tags = NULL; int update_every = default_rrd_update_every; char buf[GUID_LEN + 1]; @@ -674,12 +753,18 @@ int rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url key = value; else if(!strcmp(name, "hostname")) hostname = value; + else if(!strcmp(name, "registry_hostname")) + registry_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; + else if(!strcmp(name, "tags")) + tags = value; + else + info("STREAM [receive from [%s]:%s]: request has parameter '%s' = '%s', which is not used.", w->client_ip, w->client_port, key, value); } if(!key || !*key) { @@ -704,21 +789,21 @@ int rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url } 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); + error("STREAM [receive from [%s]:%s]: API key '%s' is not valid GUID (use the command uuidgen to generate one). 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); + error("STREAM [receive from [%s]:%s]: machine GUID '%s' is not GUID. Forbidding access.", w->client_ip, w->client_port, machine_guid); 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); + error("STREAM [receive from [%s]:%s]: API key '%s' is not allowed. Forbidding access.", w->client_ip, w->client_port, key); buffer_flush(w->response.data); buffer_sprintf(w->response.data, "Your API key is not permitted access."); return 401; @@ -732,14 +817,16 @@ int rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url } 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; + rpt->fd = w->ifd; + rpt->key = strdupz(key); + rpt->hostname = strdupz(hostname); + rpt->registry_hostname = strdupz((registry_hostname && *registry_hostname)?registry_hostname:hostname); + rpt->machine_guid = strdupz(machine_guid); + rpt->os = strdupz(os); + rpt->tags = (tags)?strdupz(tags):NULL; + 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); diff --git a/src/rrdpush.h b/src/rrdpush.h index dddbe758b..c3c7f4a54 100644 --- a/src/rrdpush.h +++ b/src/rrdpush.h @@ -4,9 +4,11 @@ extern int default_rrdpush_enabled; extern char *default_rrdpush_destination; extern char *default_rrdpush_api_key; +extern unsigned int remote_clock_resync_iterations; extern int rrdpush_init(); extern void rrdset_done_push(RRDSET *st); +extern void rrdset_push_chart_definition(RRDSET *st); extern void *rrdpush_sender_thread(void *ptr); extern int rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url); diff --git a/src/rrdset.c b/src/rrdset.c index c847b9690..caa427ff6 100644 --- a/src/rrdset.c +++ b/src/rrdset.c @@ -168,6 +168,58 @@ void rrdset_set_name(RRDSET *st, const char *name) { error("RRDSET: INTERNAL ERROR: attempted to index duplicate chart name '%s'", st->name); } +inline void rrdset_is_obsolete(RRDSET *st) { + if(unlikely(!(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)))) { + rrdset_flag_set(st, RRDSET_FLAG_OBSOLETE); + rrdset_flag_clear(st, RRDSET_FLAG_EXPOSED_UPSTREAM); + + // the chart will not get more updates (data collection) + // so, we have to push its definition now + if(unlikely(st->rrdhost->rrdpush_enabled)) + rrdset_push_chart_definition(st); + } +} + +inline void rrdset_isnot_obsolete(RRDSET *st) { + if(unlikely((rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE)))) { + rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); + rrdset_flag_clear(st, RRDSET_FLAG_EXPOSED_UPSTREAM); + + // the chart will be pushed upstream automatically + // due to data collection + } +} + +inline void rrdset_update_heterogeneous_flag(RRDSET *st) { + RRDDIM *rd; + + rrdset_flag_clear(st, RRDSET_FLAG_HOMEGENEOUS_CHECK); + + RRD_ALGORITHM algorithm = st->dimensions->algorithm; + collected_number multiplier = abs(st->dimensions->multiplier); + collected_number divisor = abs(st->dimensions->divisor); + + rrddim_foreach_read(rd, st) { + if(algorithm != rd->algorithm || multiplier != abs(rd->multiplier) || divisor != abs(rd->divisor)) { + if(!rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS)) { + #ifdef NETDATA_INTERNAL_CHECKS + info("Dimension '%s' added on chart '%s' of host '%s' is not homogeneous to other dimensions already present (algorithm is '%s' vs '%s', multiplier is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ", divisor is " COLLECTED_NUMBER_FORMAT " vs " COLLECTED_NUMBER_FORMAT ").", + rd->name, + st->name, + st->rrdhost->hostname, + rrd_algorithm_name(rd->algorithm), rrd_algorithm_name(algorithm), + rd->multiplier, multiplier, + rd->divisor, divisor + ); + #endif + rrdset_flag_set(st, RRDSET_FLAG_HETEROGENEOUS); + } + return; + } + } + + rrdset_flag_clear(st, RRDSET_FLAG_HETEROGENEOUS); +} // ---------------------------------------------------------------------------- // RRDSET - reset a chart @@ -188,7 +240,7 @@ void rrdset_reset(RRDSET *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)); + // memset(rd->values, 0, rd->entries * sizeof(storage_number)); } } @@ -199,7 +251,7 @@ 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)) + if(unlikely(mode == RRD_MEMORY_MODE_NONE || mode == RRD_MEMORY_MODE_ALLOC)) return entries; long page = (size_t)sysconf(_SC_PAGESIZE); @@ -215,14 +267,18 @@ inline long align_entries_to_pagesize(RRD_MEMORY_MODE mode, long entries) { 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_collected_time_align(RRDSET *st) { + st->last_collected_time.tv_sec -= st->last_collected_time.tv_sec % st->update_every; + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST))) + st->last_collected_time.tv_usec = 0; + else + st->last_collected_time.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; +static inline void last_updated_time_align(RRDSET *st) { + st->last_updated.tv_sec -= st->last_updated.tv_sec % st->update_every; + st->last_updated.tv_usec = 0; } // ---------------------------------------------------------------------------- @@ -279,30 +335,36 @@ void rrdset_free(RRDSET *st) { // 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); + switch(st->rrd_memory_mode) { + case RRD_MEMORY_MODE_SAVE: + case RRD_MEMORY_MODE_MAP: + case RRD_MEMORY_MODE_RAM: + debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name); + munmap(st, st->memsize); + break; + + case RRD_MEMORY_MODE_ALLOC: + case RRD_MEMORY_MODE_NONE: + freez(st); + break; } - 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); + memory_file_save(st->cache_filename, st, st->memsize); } + RRDDIM *rd; 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); + memory_file_save(rd->cache_filename, rd, rd->memsize); } } } @@ -312,19 +374,23 @@ void rrdset_delete(RRDSET *st) { rrdset_check_rdlock(st); - // info("Deleting chart '%s' ('%s')", st->id, st->name); + info("Deleting chart '%s' ('%s') from disk...", 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); + if(st->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || st->rrd_memory_mode == RRD_MEMORY_MODE_MAP) { + info("Deleting chart header file '%s'.", st->cache_filename); + if(unlikely(unlink(st->cache_filename) == -1)) + error("Cannot delete chart header file '%s'", 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); + if(likely(rd->rrd_memory_mode == RRD_MEMORY_MODE_SAVE || rd->rrd_memory_mode == RRD_MEMORY_MODE_MAP)) { + info("Deleting dimension file '%s'.", rd->cache_filename); + if(unlikely(unlink(rd->cache_filename) == -1)) + error("Cannot delete dimension file '%s'", rd->cache_filename); } } + + recursively_delete_dir(st->cache_dir, "left-over chart"); } // ---------------------------------------------------------------------------- @@ -333,7 +399,7 @@ void rrdset_delete(RRDSET *st) { 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); + rrdset_isnot_obsolete(st); debug(D_RRD_CALLS, "RRDSET '%s', already exists.", fullid); return st; } @@ -341,7 +407,7 @@ static inline RRDSET *rrdset_find_on_create(RRDHOST *host, const char *fullid) { return NULL; } -RRDSET *rrdset_create( +RRDSET *rrdset_create_custom( RRDHOST *host , const char *type , const char *id @@ -353,6 +419,8 @@ RRDSET *rrdset_create( , long priority , int update_every , RRDSET_TYPE chart_type + , RRD_MEMORY_MODE memory_mode + , long history_entries ) { if(!type || !type[0]) { fatal("Cannot create rrd stats without a type."); @@ -395,11 +463,11 @@ RRDSET *rrdset_create( // ------------------------------------------------------------------------ // 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); + long rentries = config_get_number(config_section, "history", history_entries); + long entries = align_entries_to_pagesize(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) + if(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); @@ -416,8 +484,14 @@ RRDSET *rrdset_create( 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(memory_mode == RRD_MEMORY_MODE_SAVE || memory_mode == RRD_MEMORY_MODE_MAP || memory_mode == RRD_MEMORY_MODE_RAM) { + st = (RRDSET *) mymmap( + (memory_mode == RRD_MEMORY_MODE_RAM)?NULL:fullfilename + , size + , ((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)); @@ -426,64 +500,68 @@ RRDSET *rrdset_create( memset(&st->rrdset_rwlock, 0, sizeof(netdata_rwlock_t)); st->name = NULL; + st->config_section = NULL; st->type = NULL; st->family = NULL; - st->context = NULL; st->title = NULL; st->units = NULL; + st->context = NULL; + st->cache_dir = NULL; st->dimensions = NULL; + st->rrdfamily = NULL; + st->rrdhost = 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; + if(memory_mode == RRD_MEMORY_MODE_RAM) { 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); + else { + if(strcmp(st->magic, RRDSET_MAGIC) != 0) { + info("Initializing file %s.", fullfilename); + memset(st, 0, size); + } + else if(strcmp(st->id, fullid) != 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) { + error("File %s does not have the desired size. Clearing it.", fullfilename); + memset(st, 0, size); + } + else if(st->update_every != update_every) { + 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) { + error("File %s is too old. Clearing it.", fullfilename); + memset(st, 0, size); + } + else if(st->last_updated.tv_sec > now + update_every) { + 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) { + st->update_every = update_every; + last_updated_time_align(st); + } + } // make sure we have the right memory mode // even if we cleared the memory - st->rrd_memory_mode = host->rrd_memory_mode; + st->rrd_memory_mode = 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->rrd_memory_mode = (memory_mode == RRD_MEMORY_MODE_NONE) ? RRD_MEMORY_MODE_NONE : RRD_MEMORY_MODE_ALLOC; } st->config_section = strdup(config_section); @@ -519,6 +597,7 @@ RRDSET *rrdset_create( rrdset_flag_clear(st, RRDSET_FLAG_DETAIL); rrdset_flag_clear(st, RRDSET_FLAG_DEBUG); rrdset_flag_clear(st, RRDSET_FLAG_OBSOLETE); + rrdset_flag_clear(st, RRDSET_FLAG_EXPOSED_UPSTREAM); // if(!strcmp(st->id, "disk_util.dm-0")) { // st->debug = 1; @@ -536,6 +615,9 @@ RRDSET *rrdset_create( st->gap_when_lost_iterations_above = (int) ( config_get_number(st->config_section, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2); + st->last_accessed_time = 0; + st->upstream_resync_time = 0; + avl_init_lock(&st->dimensions_index, rrddim_compare); avl_init_lock(&st->variables_root_index, rrdvar_compare); @@ -544,13 +626,8 @@ RRDSET *rrdset_create( 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->title = config_get(st->config_section, "title", title); + json_fix_string(st->title); st->rrdfamily = rrdfamily_create(host, st->family); @@ -571,7 +648,7 @@ RRDSET *rrdset_create( rrdsetcalc_link_matching(st); rrdcalctemplate_link_matching(st); - rrdhost_cleanup_obsolete(host); + rrdhost_cleanup_obsolete_charts(host); rrdhost_unlock(host); @@ -583,16 +660,10 @@ RRDSET *rrdset_create( // 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); + if(unlikely(!st->last_collected_time.tv_sec || !microseconds || (st->counter % remote_clock_resync_iterations) == 0)) { + // call the full next_usec() function + rrdset_next_usec(st, microseconds); + return; } st->usec_since_last_update = microseconds; @@ -612,52 +683,34 @@ inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) { } 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); + info("RRD database for chart '%s' on host '%s' is %0.5Lf secs in the future. Adjusting it to current time.", st->id, st->rrdhost->hostname, (long double)-since_last_usec / USEC_PER_SEC); 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); + last_collected_time_align(st); 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); + last_updated_time_align(st); 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); + else if(unlikely((usec_t)since_last_usec > (usec_t)(st->update_every * 10 * USEC_PER_SEC))) { + // oops! the database is too far behind + info("RRD database for chart '%s' on host '%s' is %0.5Lf secs in the past. Adjusting it to current time.", st->id, st->rrdhost->hostname, (long double)since_last_usec / USEC_PER_SEC); -//#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); + #ifdef NETDATA_INTERNAL_CHECKS + debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds); + rrdset_debug(st, "NEXT: %llu microseconds", microseconds); + #endif st->usec_since_last_update = microseconds; } @@ -666,9 +719,17 @@ inline void rrdset_next_usec(RRDSET *st, usec_t microseconds) { // ---------------------------------------------------------------------------- // RRDSET - process the collected values for all dimensions of a chart -static inline void rrdset_init_last_collected_time(RRDSET *st) { +static inline usec_t 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); + last_collected_time_align(st); + + usec_t last_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec; + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "initialized last collected time to %0.3Lf", (long double)last_collect_ut / USEC_PER_SEC); + #endif + + return last_collect_ut; } static inline usec_t rrdset_update_last_collected_time(RRDSET *st) { @@ -676,17 +737,42 @@ static inline usec_t rrdset_update_last_collected_time(RRDSET *st) { 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); + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "updated last collected time to %0.3Lf", (long double)last_collect_ut / USEC_PER_SEC); + #endif + return last_collect_ut; } -static inline void rrdset_init_last_updated_time(RRDSET *st) { +static inline usec_t 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); + + if(rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST)) + st->last_updated.tv_sec -= st->update_every; + + last_updated_time_align(st); + + usec_t last_updated_ut = st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec; + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "initialized last updated time to %0.3Lf", (long double)last_updated_ut / USEC_PER_SEC); + #endif + + return last_updated_ut; } static inline void rrdset_done_push_exclusive(RRDSET *st) { +// usec_t update_every_ut = st->update_every * USEC_PER_SEC; // st->update_every in microseconds +// +// if(unlikely(st->usec_since_last_update > update_every_ut * remote_clock_resync_iterations)) { +// error("Chart '%s' was last collected %llu usec before. Resetting it.", st->id, st->usec_since_last_update); +// rrdset_reset(st); +// st->usec_since_last_update = update_every_ut; +// } + if(unlikely(!st->last_collected_time.tv_sec)) { // it is the first entry // set the last_collected_time to now @@ -705,6 +791,235 @@ static inline void rrdset_done_push_exclusive(RRDSET *st) { rrdset_unlock(st); } + +static inline size_t rrdset_done_interpolate( + RRDSET *st + , usec_t update_every_ut + , usec_t last_stored_ut + , usec_t next_store_ut + , usec_t last_collect_ut + , usec_t now_collect_ut + , char store_this_entry + , uint32_t storage_flags +) { + RRDDIM *rd; + + size_t stored_entries = 0; // the number of entries we have stored in the db, during this call to rrdset_done() + + usec_t first_ut = last_stored_ut, last_ut = 0; + ssize_t iterations = (ssize_t)((now_collect_ut - last_stored_ut) / (update_every_ut)); + if((now_collect_ut % (update_every_ut)) == 0) iterations++; + + size_t counter = st->counter; + long current_entry = st->current_entry; + + 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("INTERNAL CHECK: %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); } + rrdset_debug(st, "last_stored_ut = %0.3Lf (last updated time)", (long double)last_stored_ut/USEC_PER_SEC); + rrdset_debug(st, "next_store_ut = %0.3Lf (next interpolation point)", (long double)next_store_ut/USEC_PER_SEC); + #endif + + last_ut = next_store_ut; + + 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) + ); + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: CALC2 INC " + CALCULATED_NUMBER_FORMAT " = " + CALCULATED_NUMBER_FORMAT + " * (%llu - %llu)" + " / (%llu - %llu)" + , rd->name + , new_value + , rd->calculated_value + , next_store_ut, last_collect_ut + , now_collect_ut, last_collect_ut + ); + #endif + + 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)) { + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: COLLECTION POINT IS SHORT " CALCULATED_NUMBER_FORMAT " - EXTRAPOLATING", + rd->name + , (calculated_number)(next_store_ut - last_stored_ut) + ); + #endif + + new_value = new_value * (calculated_number)(st->update_every * USEC_PER_SEC) / (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 + ); + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: CALC2 DEF " + CALCULATED_NUMBER_FORMAT " = (((" + "(" CALCULATED_NUMBER_FORMAT " - " CALCULATED_NUMBER_FORMAT ")" + " * %llu" + " / %llu) + " CALCULATED_NUMBER_FORMAT + , 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 + ); + #endif + } + break; + } + + if(unlikely(!store_this_entry)) { + rd->values[current_entry] = SN_EMPTY_SLOT; //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[current_entry] = pack_storage_number(new_value, storage_flags ); + rd->last_stored_value = new_value; + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: STORE[%ld] " + CALCULATED_NUMBER_FORMAT " = " CALCULATED_NUMBER_FORMAT + , rd->name + , current_entry + , unpack_storage_number(rd->values[current_entry]), new_value + ); + #endif + + } + else { + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: STORE[%ld] = NON EXISTING " + , rd->name + , current_entry + ); + #endif + + rd->values[current_entry] = SN_EMPTY_SLOT; // pack_storage_number(0, SN_NOT_EXISTS); + rd->last_stored_value = NAN; + } + + stored_entries++; + + #ifdef NETDATA_INTERNAL_CHECKS + 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[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 + , current_entry + , t2 + , get_storage_number_flags(rd->values[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 + , current_entry + , rd->stored_volume + , rd->collected_volume + , accuracy + , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : "" + ); + } + #endif + } + // reset the storage flags for the next point, if any; + storage_flags = SN_EXISTS; + + counter++; + current_entry = ((current_entry + 1) >= st->entries) ? 0 : current_entry + 1; + last_stored_ut = next_store_ut; + } + + st->counter = counter; + st->current_entry = current_entry; + + if(likely(last_ut)) { + st->last_updated.tv_sec = (time_t) (last_ut / USEC_PER_SEC); + st->last_updated.tv_usec = 0; + } + + return stored_entries; +} + +static inline void rrdset_done_fill_the_gap(RRDSET *st) { + usec_t update_every_ut = st->update_every * USEC_PER_SEC; + usec_t now_collect_ut = st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec; + + long c = 0, entries = st->entries; + RRDDIM *rd; + rrddim_foreach_read(rd, st) { + usec_t next_store_ut = (st->last_updated.tv_sec + st->update_every) * USEC_PER_SEC; + long current_entry = st->current_entry; + + for(c = 0; c < entries && next_store_ut <= now_collect_ut ; next_store_ut += update_every_ut, c++) { + rd->values[current_entry] = SN_EMPTY_SLOT; + current_entry = ((current_entry + 1) >= entries) ? 0 : current_entry + 1; + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: STORE[%ld] = NON EXISTING (FILLED THE GAP)", rd->name, current_entry); + #endif + } + } + + if(c > 0) { + c--; + st->last_updated.tv_sec += c * st->update_every; + + st->current_entry += c; + if(st->current_entry >= st->entries) + st->current_entry -= st->entries; + } +} + void rrdset_done(RRDSET *st) { if(unlikely(netdata_exit)) return; @@ -726,9 +1041,6 @@ void rrdset_done(RRDSET *st) { 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) @@ -742,42 +1054,33 @@ void rrdset_done(RRDSET *st) { // 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); + rrdset_isnot_obsolete(st); } // 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)); + info("host '%s', chart %s: took too long to be updated (%0.3Lf secs). Resetting it.", st->rrdhost->hostname, st->name, (long double)st->usec_since_last_update / USEC_PER_SEC); rrdset_reset(st); st->usec_since_last_update = update_every_ut; + store_this_entry = 0; 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); + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "microseconds since last update: %llu", st->usec_since_last_update); + #endif // 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; + last_collect_ut = rrdset_init_last_collected_time(st) - 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 @@ -795,9 +1098,6 @@ void rrdset_done(RRDSET *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 @@ -817,27 +1117,48 @@ void rrdset_done(RRDSET *st) { // 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; + last_stored_ut = st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.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); + // if we have not collected metrics this session (st->counter_done == 0) + // and we have collected metrics for this chart in the past (st->counter != 0) + // fill the gap (the chart has been just loaded from disk) + if(unlikely(st->counter)) { + rrdset_done_fill_the_gap(st); + last_stored_ut = st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec; + next_store_ut = (st->last_updated.tv_sec + st->update_every) * USEC_PER_SEC; + } + + if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_STORE_FIRST))) { + store_this_entry = 1; + last_collect_ut = next_store_ut - update_every_ut; + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "Fixed first entry."); + #endif + } + else { + store_this_entry = 0; + + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "Will not store the next entry."); + #endif + } } st->counter_done++; if(unlikely(st->rrdhost->rrdpush_enabled)) rrdset_done_push(st); + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "last_collect_ut = %0.3Lf (last collection time)", (long double)last_collect_ut/USEC_PER_SEC); + rrdset_debug(st, "now_collect_ut = %0.3Lf (current collection time)", (long double)now_collect_ut/USEC_PER_SEC); + rrdset_debug(st, "last_stored_ut = %0.3Lf (last updated time)", (long double)last_stored_ut/USEC_PER_SEC); + rrdset_debug(st, "next_store_ut = %0.3Lf (next interpolation point)", (long double)next_store_ut/USEC_PER_SEC); + #endif + // calculate totals and count the dimensions int dimensions = 0; st->collected_total = 0; @@ -859,18 +1180,19 @@ void rrdset_done(RRDSET *st) { 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 + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: START " + " last_collected_value = " COLLECTED_NUMBER_FORMAT + " collected_value = " COLLECTED_NUMBER_FORMAT + " last_calculated_value = " CALCULATED_NUMBER_FORMAT + " calculated_value = " CALCULATED_NUMBER_FORMAT + , rd->name , rd->last_collected_value , rd->collected_value , rd->last_calculated_value , rd->calculated_value - ); + ); + #endif switch(rd->algorithm) { case RRD_ALGORITHM_ABSOLUTE: @@ -878,18 +1200,20 @@ void rrdset_done(RRDSET *st) { * (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 " + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: CALC ABS/ABS-NO-IN " CALCULATED_NUMBER_FORMAT " = " COLLECTED_NUMBER_FORMAT " * " CALCULATED_NUMBER_FORMAT " / " CALCULATED_NUMBER_FORMAT - , st->id, rd->name + , rd->name , rd->calculated_value , rd->collected_value , (calculated_number)rd->multiplier , (calculated_number)rd->divisor - ); + ); + #endif + break; case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL: @@ -903,16 +1227,18 @@ void rrdset_done(RRDSET *st) { * (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 " + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: CALC PCENT-ROW " CALCULATED_NUMBER_FORMAT " = 100" - " * " COLLECTED_NUMBER_FORMAT + " * " COLLECTED_NUMBER_FORMAT " / " COLLECTED_NUMBER_FORMAT - , st->id, rd->name + , rd->name , rd->calculated_value , rd->collected_value , st->collected_total - ); + ); + #endif + break; case RRD_ALGORITHM_INCREMENTAL: @@ -940,19 +1266,21 @@ void rrdset_done(RRDSET *st) { * (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 " + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: CALC INC PRE " CALCULATED_NUMBER_FORMAT " = (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")" " * " CALCULATED_NUMBER_FORMAT " / " CALCULATED_NUMBER_FORMAT - , st->id, rd->name + , rd->name , rd->calculated_value , rd->collected_value, rd->last_collected_value , (calculated_number)rd->multiplier , (calculated_number)rd->divisor - ); + ); + #endif + break; case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL: @@ -967,7 +1295,8 @@ void rrdset_done(RRDSET *st) { 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); + , rd->collected_value + ); if(!(rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS))) storage_flags = SN_EXISTS_RESET; @@ -985,16 +1314,18 @@ void rrdset_done(RRDSET *st) { * (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 " + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: CALC PCENT-DIFF " CALCULATED_NUMBER_FORMAT " = 100" - " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")" - " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")" - , st->id, rd->name + " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")" + " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")" + , rd->name , rd->calculated_value , rd->collected_value, rd->last_collected_value , st->collected_total, st->last_collected_total - ); + ); + #endif + break; default: @@ -1002,203 +1333,53 @@ void rrdset_done(RRDSET *st) { // 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 " + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: CALC " CALCULATED_NUMBER_FORMAT " = 0" - , st->id, rd->name + , rd->name , rd->calculated_value - ); + ); + #endif + break; } - if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) - debug(D_RRD_STATS, "%s/%s: PHASE2 " + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%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->name , rd->last_collected_value , rd->collected_value , rd->last_calculated_value , rd->calculated_value - ); + ); + #endif } // at this point we have all the calculated values ready // it is now time to interpolate values on a second boundary +#ifdef NETDATA_INTERNAL_CHECKS 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; + rrdset_debug(st, "THIS IS IN THE SAME INTERPOLATION POINT"); + info("INTERNAL CHECK: host '%s', chart '%s' is collected in the same interpolation point: short by %llu microseconds", st->rrdhost->hostname, st->name, next_store_ut - now_collect_ut); } +#endif + + rrdset_done_interpolate(st + , update_every_ut + , last_stored_ut + , next_store_ut + , last_collect_ut + , now_collect_ut + , store_this_entry + , storage_flags + ); st->last_collected_total = st->collected_total; @@ -1206,29 +1387,35 @@ void rrdset_done(RRDSET *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); + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", rd->name, rd->last_collected_value, rd->collected_value); + #endif 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); + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", rd->name, rd->last_calculated_value + rd->calculated_value, rd->calculated_value); + #endif + 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); + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "THIS IS THE FIRST POINT"); + #endif } 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); + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", rd->name, rd->last_calculated_value, rd->calculated_value); + #endif + rd->last_calculated_value = rd->calculated_value; break; } @@ -1237,18 +1424,20 @@ void rrdset_done(RRDSET *st) { rd->collected_value = 0; rd->updated = 0; - if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG))) - debug(D_RRD_STATS, "%s/%s: END " + #ifdef NETDATA_INTERNAL_CHECKS + rrdset_debug(st, "%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->name , rd->last_collected_value , rd->collected_value , rd->last_calculated_value , rd->calculated_value - ); + ); + #endif + } // ALL DONE ABOUT THE DATA UPDATE diff --git a/src/socket.c b/src/socket.c index 400c1ef4e..2b3821190 100644 --- a/src/socket.c +++ b/src/socket.c @@ -1,44 +1,267 @@ #include "common.h" -// connect_to() -// -// definition format: -// -// [PROTOCOL:]IP[%INTERFACE][:PORT] -// -// PROTOCOL = tcp or udp -// IP = IPv4 or IPv6 IP or hostname, optionally enclosed in [] (required for IPv6) -// INTERFACE = for IPv6 only, the network interface to use -// PORT = port number or service name +// -------------------------------------------------------------------------------------------------------------------- +// various library calls -int connect_to(const char *definition, int default_port, struct timeval *timeout) { +#ifdef __gnu_linux__ +#define LARGE_SOCK_SIZE 33554431 // don't ask why - I found it at brubeck source - I guess it is just a large number +#else +#define LARGE_SOCK_SIZE 4096 +#endif + +int sock_setnonblock(int fd) { + int flags; + + flags = fcntl(fd, F_GETFL); + flags |= O_NONBLOCK; + + int ret = fcntl(fd, F_SETFL, flags); + if(ret < 0) + error("Failed to set O_NONBLOCK on socket %d", fd); + + return ret; +} + +int sock_delnonblock(int fd) { + int flags; + + flags = fcntl(fd, F_GETFL); + flags &= ~O_NONBLOCK; + + int ret = fcntl(fd, F_SETFL, flags); + if(ret < 0) + error("Failed to remove O_NONBLOCK on socket %d", fd); + + return ret; +} + +int sock_setreuse(int fd, int reuse) { + int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + if(ret == -1) + error("Failed to set SO_REUSEADDR on socket %d", fd); + + return ret; +} + +int sock_setreuse_port(int fd, int reuse) { + int ret = -1; +#ifdef SO_REUSEPORT + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)); + if(ret == -1) + error("failed to set SO_REUSEPORT on socket %d", fd); +#endif + + return ret; +} + +int sock_enlarge_in(int fd) { + int ret, bs = LARGE_SOCK_SIZE; + + ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bs, sizeof(bs)); + + if(ret == -1) + error("Failed to set SO_RCVBUF on socket %d", fd); + + return ret; +} + +int sock_enlarge_out(int fd) { + int ret, bs = LARGE_SOCK_SIZE; + ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bs, sizeof(bs)); + + if(ret == -1) + error("Failed to set SO_SNDBUF on socket %d", fd); + + return ret; +} + + +// -------------------------------------------------------------------------------------------------------------------- +// listening sockets + +int create_listen_socket4(int socktype, const char *ip, int port, int listen_backlog) { + int sock; + + debug(D_LISTENER, "LISTENER: IPv4 creating new listening socket on ip '%s' port %d, socktype %d", ip, port, socktype); + + sock = socket(AF_INET, socktype, 0); + if(sock < 0) { + error("LISTENER: IPv4 socket() on ip '%s' port %d, socktype %d failed.", ip, port, socktype); + return -1; + } + + sock_setreuse(sock, 1); + sock_setreuse_port(sock, 1); + sock_setnonblock(sock); + sock_enlarge_in(sock); + + struct sockaddr_in name; + memset(&name, 0, sizeof(struct sockaddr_in)); + name.sin_family = AF_INET; + name.sin_port = htons (port); + + int ret = inet_pton(AF_INET, ip, (void *)&name.sin_addr.s_addr); + if(ret != 1) { + error("LISTENER: Failed to convert IP '%s' to a valid IPv4 address.", ip); + close(sock); + return -1; + } + + if(bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { + close(sock); + error("LISTENER: IPv4 bind() on ip '%s' port %d, socktype %d failed.", ip, port, socktype); + return -1; + } + + if(socktype == SOCK_STREAM && listen(sock, listen_backlog) < 0) { + close(sock); + error("LISTENER: IPv4 listen() on ip '%s' port %d, socktype %d failed.", ip, port, socktype); + return -1; + } + + debug(D_LISTENER, "LISTENER: Listening on IPv4 ip '%s' port %d, socktype %d", ip, port, socktype); + return sock; +} + +int create_listen_socket6(int socktype, uint32_t scope_id, const char *ip, int port, int listen_backlog) { + int sock; + int ipv6only = 1; + + debug(D_LISTENER, "LISTENER: IPv6 creating new listening socket on ip '%s' port %d, socktype %d", ip, port, socktype); + + sock = socket(AF_INET6, socktype, 0); + if (sock < 0) { + error("LISTENER: IPv6 socket() on ip '%s' port %d, socktype %d, failed.", ip, port, socktype); + return -1; + } + + sock_setreuse(sock, 1); + sock_setreuse_port(sock, 1); + sock_setnonblock(sock); + sock_enlarge_in(sock); + + /* IPv6 only */ + if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&ipv6only, sizeof(ipv6only)) != 0) + error("LISTENER: Cannot set IPV6_V6ONLY on ip '%s' port %d, socktype %d.", ip, port, socktype); + + struct sockaddr_in6 name; + memset(&name, 0, sizeof(struct sockaddr_in6)); + name.sin6_family = AF_INET6; + name.sin6_port = htons ((uint16_t) port); + name.sin6_scope_id = scope_id; + + int ret = inet_pton(AF_INET6, ip, (void *)&name.sin6_addr.s6_addr); + if(ret != 1) { + error("LISTENER: Failed to convert IP '%s' to a valid IPv6 address.", ip); + close(sock); + return -1; + } + + name.sin6_scope_id = scope_id; + + if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { + close(sock); + error("LISTENER: IPv6 bind() on ip '%s' port %d, socktype %d failed.", ip, port, socktype); + return -1; + } + + if (socktype == SOCK_STREAM && listen(sock, listen_backlog) < 0) { + close(sock); + error("LISTENER: IPv6 listen() on ip '%s' port %d, socktype %d failed.", ip, port, socktype); + return -1; + } + + debug(D_LISTENER, "LISTENER: Listening on IPv6 ip '%s' port %d, socktype %d", ip, port, socktype); + return sock; +} + +static inline int listen_sockets_add(LISTEN_SOCKETS *sockets, int fd, int socktype, const char *protocol, const char *ip, int port) { + if(sockets->opened >= MAX_LISTEN_FDS) { + error("LISTENER: Too many listening sockets. Failed to add listening %s socket at ip '%s' port %d, protocol %s, socktype %d", protocol, ip, port, protocol, socktype); + close(fd); + return -1; + } + + sockets->fds[sockets->opened] = fd; + + char buffer[100 + 1]; + snprintfz(buffer, 100, "%s:[%s]:%d", protocol, ip, port); + sockets->fds_names[sockets->opened] = strdupz(buffer); + sockets->fds_types[sockets->opened] = socktype; + + sockets->opened++; + return 0; +} + +int listen_sockets_check_is_member(LISTEN_SOCKETS *sockets, int fd) { + size_t i; + for(i = 0; i < sockets->opened ;i++) + if(sockets->fds[i] == fd) return 1; + + return 0; +} + +static inline void listen_sockets_init(LISTEN_SOCKETS *sockets) { + size_t i; + for(i = 0; i < MAX_LISTEN_FDS ;i++) { + sockets->fds[i] = -1; + sockets->fds_names[i] = NULL; + sockets->fds_types[i] = -1; + } + + sockets->opened = 0; + sockets->failed = 0; +} + +void listen_sockets_close(LISTEN_SOCKETS *sockets) { + size_t i; + for(i = 0; i < sockets->opened ;i++) { + close(sockets->fds[i]); + sockets->fds[i] = -1; + + freez(sockets->fds_names[i]); + sockets->fds_names[i] = NULL; + + sockets->fds_types[i] = -1; + } + + sockets->opened = 0; + sockets->failed = 0; +} + +static inline int bind_to_one(LISTEN_SOCKETS *sockets, const char *definition, int default_port, int listen_backlog) { + int added = 0; struct addrinfo hints; - struct addrinfo *ai_head = NULL, *ai = NULL; + struct addrinfo *result = NULL, *rp = NULL; char buffer[strlen(definition) + 1]; strcpy(buffer, definition); - char default_service[10 + 1]; - snprintfz(default_service, 10, "%d", default_port); + char buffer2[10 + 1]; + snprintfz(buffer2, 10, "%d", default_port); + + char *ip = buffer, *port = buffer2, *interface = "";; - char *host = buffer, *service = default_service, *interface = ""; int protocol = IPPROTO_TCP, socktype = SOCK_STREAM; - uint32_t scope_id = 0; + const char *protocol_str = "tcp"; - if(strncmp(host, "tcp:", 4) == 0) { - host += 4; + if(strncmp(ip, "tcp:", 4) == 0) { + ip += 4; protocol = IPPROTO_TCP; socktype = SOCK_STREAM; + protocol_str = "tcp"; } - else if(strncmp(host, "udp:", 4) == 0) { - host += 4; + else if(strncmp(ip, "udp:", 4) == 0) { + ip += 4; protocol = IPPROTO_UDP; socktype = SOCK_DGRAM; + protocol_str = "udp"; } - char *e = host; + char *e = ip; if(*e == '[') { - e = ++host; + e = ++ip; while(*e && *e != ']') e++; if(*e == ']') { *e = '\0'; @@ -57,26 +280,142 @@ int connect_to(const char *definition, int default_port, struct timeval *timeout } if(*e == ':') { + port = e + 1; *e = '\0'; - e++; - service = e; } - debug(D_CONNECT_TO, "Attempting connection to host = '%s', service = '%s', interface = '%s', protocol = %d (tcp = %d, udp = %d)", host, service, interface, protocol, IPPROTO_TCP, IPPROTO_UDP); + uint32_t scope_id = 0; + if(*interface) { + scope_id = if_nametoindex(interface); + if(!scope_id) + error("LISTENER: Cannot find a network interface named '%s'. Continuing with limiting the network interface", interface); + } - if(!*host) { - error("Definition '%s' does not specify a host.", definition); + if(!*ip || *ip == '*' || !strcmp(ip, "any") || !strcmp(ip, "all")) + ip = NULL; + + if(!*port) + port = buffer2; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = socktype; + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = protocol; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + int r = getaddrinfo(ip, port, &hints, &result); + if (r != 0) { + error("LISTENER: getaddrinfo('%s', '%s'): %s\n", ip, port, gai_strerror(r)); return -1; } - if(*interface) { - scope_id = if_nametoindex(interface); - if(!scope_id) - error("Cannot find a network interface named '%s'. Continuing with limiting the network interface", interface); + for (rp = result; rp != NULL; rp = rp->ai_next) { + int fd = -1; + + char rip[INET_ADDRSTRLEN + INET6_ADDRSTRLEN] = "INVALID"; + int rport = default_port; + + switch (rp->ai_addr->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *) rp->ai_addr; + inet_ntop(AF_INET, &sin->sin_addr, rip, INET_ADDRSTRLEN); + rport = ntohs(sin->sin_port); + // info("Attempting to listen on IPv4 '%s' ('%s'), port %d ('%s'), socktype %d", rip, ip, rport, port, socktype); + fd = create_listen_socket4(socktype, rip, rport, listen_backlog); + break; + } + + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) rp->ai_addr; + inet_ntop(AF_INET6, &sin6->sin6_addr, rip, INET6_ADDRSTRLEN); + rport = ntohs(sin6->sin6_port); + // info("Attempting to listen on IPv6 '%s' ('%s'), port %d ('%s'), socktype %d", rip, ip, rport, port, socktype); + fd = create_listen_socket6(socktype, scope_id, rip, rport, listen_backlog); + break; + } + + default: + debug(D_LISTENER, "LISTENER: Unknown socket family %d", rp->ai_addr->sa_family); + break; + } + + if (fd == -1) { + error("LISTENER: Cannot bind to ip '%s', port %d", rip, rport); + sockets->failed++; + } + else { + listen_sockets_add(sockets, fd, socktype, protocol_str, rip, rport); + added++; + } } - if(!*service) - service = default_service; + freeaddrinfo(result); + + return added; +} + +int listen_sockets_setup(LISTEN_SOCKETS *sockets) { + listen_sockets_init(sockets); + + sockets->backlog = (int) config_get_number(sockets->config_section, "listen backlog", sockets->backlog); + + int old_port = sockets->default_port; + sockets->default_port = (int) config_get_number(sockets->config_section, "default port", sockets->default_port); + if(sockets->default_port < 1 || sockets->default_port > 65535) { + error("LISTENER: Invalid listen port %d given. Defaulting to %d.", sockets->default_port, old_port); + sockets->default_port = (int) config_set_number(sockets->config_section, "default port", old_port); + } + debug(D_OPTIONS, "LISTENER: Default listen port set to %d.", sockets->default_port); + + char *s = config_get(sockets->config_section, "bind to", sockets->default_bind_to); + while(*s) { + char *e = s; + + // skip separators, moving both s(tart) and e(nd) + while(isspace(*e) || *e == ',') s = ++e; + + // move e(nd) to the first separator + while(*e && !isspace(*e) && *e != ',') e++; + + // is there anything? + if(!*s || s == e) break; + + char buf[e - s + 1]; + strncpyz(buf, s, e - s); + bind_to_one(sockets, buf, sockets->default_port, sockets->backlog); + + s = e; + } + + if(sockets->failed) { + size_t i; + for(i = 0; i < sockets->opened ;i++) + info("LISTENER: Listen socket %s opened successfully.", sockets->fds_names[i]); + } + + return (int)sockets->opened; +} + + +// -------------------------------------------------------------------------------------------------------------------- +// connect to another host/port + +// _connect_to() +// protocol IPPROTO_TCP, IPPROTO_UDP +// socktype SOCK_STREAM, SOCK_DGRAM +// host the destination hostname or IP address (IPv4 or IPv6) to connect to +// if it resolves to many IPs, all are tried (IPv4 and IPv6) +// scope_id the if_index id of the interface to use for connecting (0 = any) +// (used only under IPv6) +// service the service name or port to connect to +// timeout the timeout for establishing a connection + +static inline int _connect_to(int protocol, int socktype, const char *host, uint32_t scope_id, const char *service, struct timeval *timeout) { + struct addrinfo hints; + struct addrinfo *ai_head = NULL, *ai = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; /* Allow IPv4 or IPv6 */ @@ -103,52 +442,52 @@ int connect_to(const char *definition, int default_port, struct timeval *timeout char servBfr[NI_MAXSERV + 1]; getnameinfo(ai->ai_addr, - ai->ai_addrlen, - hostBfr, - sizeof(hostBfr), - servBfr, - sizeof(servBfr), - NI_NUMERICHOST | NI_NUMERICSERV); + ai->ai_addrlen, + hostBfr, + sizeof(hostBfr), + servBfr, + sizeof(servBfr), + NI_NUMERICHOST | NI_NUMERICSERV); debug(D_CONNECT_TO, "Address info: host = '%s', service = '%s', ai_flags = 0x%02X, ai_family = %d (PF_INET = %d, PF_INET6 = %d), ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = %d), ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = %d), ai_addrlen = %lu (sockaddr_in = %lu, sockaddr_in6 = %lu)", - hostBfr, - servBfr, - (unsigned int)ai->ai_flags, - ai->ai_family, - PF_INET, - PF_INET6, - ai->ai_socktype, - SOCK_STREAM, - SOCK_DGRAM, - ai->ai_protocol, - IPPROTO_TCP, - IPPROTO_UDP, - (unsigned long)ai->ai_addrlen, - (unsigned long)sizeof(struct sockaddr_in), - (unsigned long)sizeof(struct sockaddr_in6)); + hostBfr, + servBfr, + (unsigned int)ai->ai_flags, + ai->ai_family, + PF_INET, + PF_INET6, + ai->ai_socktype, + SOCK_STREAM, + SOCK_DGRAM, + ai->ai_protocol, + IPPROTO_TCP, + IPPROTO_UDP, + (unsigned long)ai->ai_addrlen, + (unsigned long)sizeof(struct sockaddr_in), + (unsigned long)sizeof(struct sockaddr_in6)); switch (ai->ai_addr->sa_family) { case PF_INET: { struct sockaddr_in *pSadrIn = (struct sockaddr_in *)ai->ai_addr; debug(D_CONNECT_TO, "ai_addr = sin_family: %d (AF_INET = %d, AF_INET6 = %d), sin_addr: '%s', sin_port: '%s'", - pSadrIn->sin_family, - AF_INET, - AF_INET6, - hostBfr, - servBfr); + pSadrIn->sin_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr); break; } case PF_INET6: { struct sockaddr_in6 *pSadrIn6 = (struct sockaddr_in6 *) ai->ai_addr; debug(D_CONNECT_TO,"ai_addr = sin6_family: %d (AF_INET = %d, AF_INET6 = %d), sin6_addr: '%s', sin6_port: '%s', sin6_flowinfo: %u, sin6_scope_id: %u", - pSadrIn6->sin6_family, - AF_INET, - AF_INET6, - hostBfr, - servBfr, - pSadrIn6->sin6_flowinfo, - pSadrIn6->sin6_scope_id); + pSadrIn6->sin6_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr, + pSadrIn6->sin6_flowinfo, + pSadrIn6->sin6_scope_id); break; } @@ -180,6 +519,85 @@ int connect_to(const char *definition, int default_port, struct timeval *timeout return fd; } +// connect_to() +// +// definition format: +// +// [PROTOCOL:]IP[%INTERFACE][:PORT] +// +// PROTOCOL = tcp or udp +// IP = IPv4 or IPv6 IP or hostname, optionally enclosed in [] (required for IPv6) +// INTERFACE = for IPv6 only, the network interface to use +// PORT = port number or service name + +int connect_to(const char *definition, int default_port, struct timeval *timeout) { + char buffer[strlen(definition) + 1]; + strcpy(buffer, definition); + + char default_service[10 + 1]; + snprintfz(default_service, 10, "%d", default_port); + + char *host = buffer, *service = default_service, *interface = ""; + int protocol = IPPROTO_TCP, socktype = SOCK_STREAM; + uint32_t scope_id = 0; + + if(strncmp(host, "tcp:", 4) == 0) { + host += 4; + protocol = IPPROTO_TCP; + socktype = SOCK_STREAM; + } + else if(strncmp(host, "udp:", 4) == 0) { + host += 4; + protocol = IPPROTO_UDP; + socktype = SOCK_DGRAM; + } + + char *e = host; + if(*e == '[') { + e = ++host; + while(*e && *e != ']') e++; + if(*e == ']') { + *e = '\0'; + e++; + } + } + else { + while(*e && *e != ':' && *e != '%') e++; + } + + if(*e == '%') { + *e = '\0'; + e++; + interface = e; + while(*e && *e != ':') e++; + } + + if(*e == ':') { + *e = '\0'; + e++; + service = e; + } + + debug(D_CONNECT_TO, "Attempting connection to host = '%s', service = '%s', interface = '%s', protocol = %d (tcp = %d, udp = %d)", host, service, interface, protocol, IPPROTO_TCP, IPPROTO_UDP); + + if(!*host) { + error("Definition '%s' does not specify a host.", definition); + return -1; + } + + if(*interface) { + scope_id = if_nametoindex(interface); + if(!scope_id) + error("Cannot find a network interface named '%s'. Continuing with limiting the network interface", interface); + } + + if(!*service) + service = default_service; + + + return _connect_to(protocol, socktype, host, scope_id, service, timeout); +} + 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; @@ -213,6 +631,10 @@ int connect_to_one_of(const char *destination, int default_port, struct timeval return sock; } + +// -------------------------------------------------------------------------------------------------------------------- +// helpers to send/receive data in one call, in blocking mode, with a timeout + ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) { for(;;) { struct pollfd fd = { @@ -274,3 +696,455 @@ ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) return send(sockfd, buf, len, flags); } + + +// -------------------------------------------------------------------------------------------------------------------- +// accept4() replacement for systems that do not have one + +#ifndef HAVE_ACCEPT4 +int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags) { + int fd = accept(sock, addr, addrlen); + int newflags = 0; + + if (fd < 0) return fd; + + if (flags & SOCK_NONBLOCK) { + newflags |= O_NONBLOCK; + flags &= ~SOCK_NONBLOCK; + } + +#ifdef SOCK_CLOEXEC +#ifdef O_CLOEXEC + if (flags & SOCK_CLOEXEC) { + newflags |= O_CLOEXEC; + flags &= ~SOCK_CLOEXEC; + } +#endif +#endif + + if (flags) { + errno = -EINVAL; + return -1; + } + + if (fcntl(fd, F_SETFL, newflags) < 0) { + int saved_errno = errno; + close(fd); + errno = saved_errno; + return -1; + } + + return fd; +} +#endif + + +// -------------------------------------------------------------------------------------------------------------------- +// accept_socket() - accept a socket and store client IP and port + +int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *client_port, size_t portsize) { + struct sockaddr_storage sadr; + socklen_t addrlen = sizeof(sadr); + + int nfd = accept4(fd, (struct sockaddr *)&sadr, &addrlen, flags); + if (nfd >= 0) { + if (getnameinfo((struct sockaddr *)&sadr, addrlen, client_ip, (socklen_t)ipsize, client_port, (socklen_t)portsize, NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + error("LISTENER: cannot getnameinfo() on received client connection."); + strncpyz(client_ip, "UNKNOWN", ipsize - 1); + strncpyz(client_port, "UNKNOWN", portsize - 1); + } + + client_ip[ipsize - 1] = '\0'; + client_port[portsize - 1] = '\0'; + + switch (((struct sockaddr *)&sadr)->sa_family) { + case AF_INET: + debug(D_LISTENER, "New IPv4 web client from %s port %s on socket %d.", client_ip, client_port, fd); + break; + + case AF_INET6: + if (strncmp(client_ip, "::ffff:", 7) == 0) { + memmove(client_ip, &client_ip[7], strlen(&client_ip[7]) + 1); + debug(D_LISTENER, "New IPv4 web client from %s port %s on socket %d.", client_ip, client_port, fd); + } else + debug(D_LISTENER, "New IPv6 web client from %s port %s on socket %d.", client_ip, client_port, fd); + break; + + default: + debug(D_LISTENER, "New UNKNOWN web client from %s port %s on socket %d.", client_ip, client_port, fd); + break; + } + } + + return nfd; +} + + +// -------------------------------------------------------------------------------------------------------------------- +// poll() based listener +// this should be the fastest possible listener for up to 100 sockets +// above 100, an epoll() interface is needed on Linux + +#define POLL_FDS_INCREASE_STEP 10 + +#define POLLINFO_FLAG_SERVER_SOCKET 0x00000001 +#define POLLINFO_FLAG_CLIENT_SOCKET 0x00000002 + +struct pollinfo { + size_t slot; + char *client; + struct pollinfo *next; + uint32_t flags; + int socktype; + + void *data; +}; + +struct poll { + size_t slots; + size_t used; + size_t min; + size_t max; + struct pollfd *fds; + struct pollinfo *inf; + struct pollinfo *first_free; + + void *(*add_callback)(int fd, short int *events); + void (*del_callback)(int fd, void *data); + int (*rcv_callback)(int fd, int socktype, void *data, short int *events); + int (*snd_callback)(int fd, int socktype, void *data, short int *events); +}; + +static inline struct pollinfo *poll_add_fd(struct poll *p, int fd, int socktype, short int events, uint32_t flags) { + debug(D_POLLFD, "POLLFD: ADD: request to add fd %d, slots = %zu, used = %zu, min = %zu, max = %zu, next free = %zd", fd, p->slots, p->used, p->min, p->max, p->first_free?(ssize_t)p->first_free->slot:(ssize_t)-1); + + if(unlikely(fd < 0)) return NULL; + + if(unlikely(!p->first_free)) { + size_t new_slots = p->slots + POLL_FDS_INCREASE_STEP; + debug(D_POLLFD, "POLLFD: ADD: increasing size (current = %zu, new = %zu, used = %zu, min = %zu, max = %zu)", p->slots, new_slots, p->used, p->min, p->max); + + p->fds = reallocz(p->fds, sizeof(struct pollfd) * new_slots); + p->inf = reallocz(p->inf, sizeof(struct pollinfo) * new_slots); + + ssize_t i; + for(i = new_slots - 1; i >= (ssize_t)p->slots ; i--) { + debug(D_POLLFD, "POLLFD: ADD: resetting new slot %zd", i); + p->fds[i].fd = -1; + p->fds[i].events = 0; + p->fds[i].revents = 0; + + p->inf[i].slot = (size_t)i; + p->inf[i].flags = 0; + p->inf[i].socktype = -1; + p->inf[i].client = NULL; + p->inf[i].data = NULL; + p->inf[i].next = p->first_free; + p->first_free = &p->inf[i]; + } + + p->slots = new_slots; + } + + struct pollinfo *pi = p->first_free; + p->first_free = p->first_free->next; + + debug(D_POLLFD, "POLLFD: ADD: selected slot %zu, next free is %zd", pi->slot, p->first_free?(ssize_t)p->first_free->slot:(ssize_t)-1); + + struct pollfd *pf = &p->fds[pi->slot]; + pf->fd = fd; + pf->events = events; + pf->revents = 0; + + pi->socktype = socktype; + pi->flags = flags; + pi->next = NULL; + + p->used++; + if(unlikely(pi->slot > p->max)) + p->max = pi->slot; + + if(pi->flags & POLLINFO_FLAG_CLIENT_SOCKET) { + pi->data = p->add_callback(fd, &pf->events); + } + + if(pi->flags & POLLINFO_FLAG_SERVER_SOCKET) { + p->min = pi->slot; + } + + debug(D_POLLFD, "POLLFD: ADD: completed, slots = %zu, used = %zu, min = %zu, max = %zu, next free = %zd", p->slots, p->used, p->min, p->max, p->first_free?(ssize_t)p->first_free->slot:(ssize_t)-1); + + return pi; +} + +static inline void poll_close_fd(struct poll *p, struct pollinfo *pi) { + struct pollfd *pf = &p->fds[pi->slot]; + debug(D_POLLFD, "POLLFD: DEL: request to clear slot %zu (fd %d), old next free was %zd", pi->slot, pf->fd, p->first_free?(ssize_t)p->first_free->slot:(ssize_t)-1); + + if(unlikely(pf->fd == -1)) return; + + if(pi->flags & POLLINFO_FLAG_CLIENT_SOCKET) { + p->del_callback(pf->fd, pi->data); + } + + close(pf->fd); + pf->fd = -1; + pf->events = 0; + pf->revents = 0; + + pi->socktype = -1; + pi->flags = 0; + pi->data = NULL; + + freez(pi->client); + pi->client = NULL; + + pi->next = p->first_free; + p->first_free = pi; + + p->used--; + if(p->max == pi->slot) { + p->max = p->min; + ssize_t i; + for(i = (ssize_t)pi->slot; i > (ssize_t)p->min ;i--) { + if (unlikely(p->fds[i].fd != -1)) { + p->max = (size_t)i; + break; + } + } + } + + debug(D_POLLFD, "POLLFD: DEL: completed, slots = %zu, used = %zu, min = %zu, max = %zu, next free = %zd", p->slots, p->used, p->min, p->max, p->first_free?(ssize_t)p->first_free->slot:(ssize_t)-1); +} + +static void *add_callback_default(int fd, short int *events) { + (void)fd; + (void)events; + + return NULL; +} +static void del_callback_default(int fd, void *data) { + (void)fd; + (void)data; + + if(data) + error("POLLFD: internal error: del_callback_default() called with data pointer - possible memory leak"); +} + +static int rcv_callback_default(int fd, int socktype, void *data, short int *events) { + (void)socktype; + (void)data; + (void)events; + + char buffer[1024 + 1]; + + ssize_t rc; + do { + rc = recv(fd, buffer, 1024, MSG_DONTWAIT); + if (rc < 0) { + // read failed + if (errno != EWOULDBLOCK && errno != EAGAIN) { + error("POLLFD: recv() failed."); + return -1; + } + } else if (rc) { + // data received + info("POLLFD: internal error: discarding %zd bytes received on socket %d", rc, fd); + } + } while (rc != -1); + + return 0; +} + +static int snd_callback_default(int fd, int socktype, void *data, short int *events) { + (void)socktype; + (void)data; + (void)events; + + *events &= ~POLLOUT; + + info("POLLFD: internal error: nothing to send on socket %d", fd); + return 0; +} + +void poll_events_cleanup(void *data) { + struct poll *p = (struct poll *)data; + + size_t i; + for(i = 0 ; i <= p->max ; i++) { + struct pollinfo *pi = &p->inf[i]; + poll_close_fd(p, pi); + } + + freez(p->fds); + freez(p->inf); +} + +void poll_events(LISTEN_SOCKETS *sockets + , void *(*add_callback)(int fd, short int *events) + , void (*del_callback)(int fd, void *data) + , int (*rcv_callback)(int fd, int socktype, void *data, short int *events) + , int (*snd_callback)(int fd, int socktype, void *data, short int *events) + , void *data +) { + int retval; + + struct poll p = { + .slots = 0, + .used = 0, + .max = 0, + .fds = NULL, + .inf = NULL, + .first_free = NULL, + + .add_callback = add_callback?add_callback:add_callback_default, + .del_callback = del_callback?del_callback:del_callback_default, + .rcv_callback = rcv_callback?rcv_callback:rcv_callback_default, + .snd_callback = snd_callback?snd_callback:snd_callback_default + }; + + size_t i; + for(i = 0; i < sockets->opened ;i++) { + struct pollinfo *pi = poll_add_fd(&p, sockets->fds[i], sockets->fds_types[i], POLLIN, POLLINFO_FLAG_SERVER_SOCKET); + pi->data = data; + info("POLLFD: LISTENER: listening on '%s'", (sockets->fds_names[i])?sockets->fds_names[i]:"UNKNOWN"); + } + + int timeout = -1; // wait forever + + pthread_cleanup_push(poll_events_cleanup, &p); + + for(;;) { + if(unlikely(netdata_exit)) break; + + debug(D_POLLFD, "POLLFD: LISTENER: Waiting on %zu sockets...", p.max + 1); + retval = poll(p.fds, p.max + 1, timeout); + + if(unlikely(retval == -1)) { + error("POLLFD: LISTENER: poll() failed."); + continue; + } + else if(unlikely(!retval)) { + debug(D_POLLFD, "POLLFD: LISTENER: poll() timeout."); + continue; + } + + if(unlikely(netdata_exit)) break; + + for(i = 0 ; i <= p.max ; i++) { + struct pollfd *pf = &p.fds[i]; + struct pollinfo *pi = &p.inf[i]; + int fd = pf->fd; + short int revents = pf->revents; + pf->revents = 0; + + if(unlikely(fd == -1)) { + debug(D_POLLFD, "POLLFD: LISTENER: ignoring slot %zu, it does not have an fd", i); + continue; + } + + debug(D_POLLFD, "POLLFD: LISTENER: processing events for slot %zu (events = %d, revents = %d)", i, pf->events, revents); + + if(revents & POLLIN || revents & POLLPRI) { + // receiving data + + if(likely(pi->flags & POLLINFO_FLAG_SERVER_SOCKET)) { + // new connection + // debug(D_POLLFD, "POLLFD: LISTENER: accepting connections from slot %zu (fd %d)", i, fd); + + switch(pi->socktype) { + case SOCK_STREAM: { + // a TCP socket + // we accept the connection + + int nfd; + do { + char client_ip[NI_MAXHOST + 1]; + char client_port[NI_MAXSERV + 1]; + + debug(D_POLLFD, "POLLFD: LISTENER: calling accept4() slot %zu (fd %d)", i, fd); + nfd = accept_socket(fd, SOCK_NONBLOCK, client_ip, NI_MAXHOST + 1, client_port, NI_MAXSERV + 1); + if (nfd < 0) { + // accept failed + + debug(D_POLLFD, "POLLFD: LISTENER: accept4() slot %zu (fd %d) failed.", i, fd); + + if(errno != EWOULDBLOCK && errno != EAGAIN) + error("POLLFD: LISTENER: accept() failed."); + + break; + } + else { + // accept ok + info("POLLFD: LISTENER: client '[%s]:%s' connected to '%s'", client_ip, client_port, sockets->fds_names[i]); + poll_add_fd(&p, nfd, SOCK_STREAM, POLLIN, POLLINFO_FLAG_CLIENT_SOCKET); + + // it may have realloced them, so refresh our pointers + pf = &p.fds[i]; + pi = &p.inf[i]; + } + } while (nfd != -1); + break; + } + + case SOCK_DGRAM: { + // a UDP socket + // we read data from the server socket + + debug(D_POLLFD, "POLLFD: LISTENER: reading data from UDP slot %zu (fd %d)", i, fd); + + p.rcv_callback(fd, pi->socktype, pi->data, &pf->events); + break; + } + + default: { + error("POLLFD: LISTENER: Unknown socktype %d on slot %zu", pi->socktype, pi->slot); + break; + } + } + } + + if(likely(pi->flags & POLLINFO_FLAG_CLIENT_SOCKET)) { + // read data from client TCP socket + debug(D_POLLFD, "POLLFD: LISTENER: reading data from TCP client slot %zu (fd %d)", i, fd); + + if (p.rcv_callback(fd, pi->socktype, pi->data, &pf->events) == -1) { + poll_close_fd(&p, pi); + continue; + } + } + } + + if(unlikely(revents & POLLOUT)) { + // sending data + debug(D_POLLFD, "POLLFD: LISTENER: sending data to socket on slot %zu (fd %d)", i, fd); + + if (p.snd_callback(fd, pi->socktype, pi->data, &pf->events) == -1) { + poll_close_fd(&p, pi); + continue; + } + } + + if(unlikely(revents & POLLERR)) { + error("POLLFD: LISTENER: processing POLLERR events for slot %zu (events = %d, revents = %d)", i, pf->events, revents); + poll_close_fd(&p, pi); + continue; + } + + if(unlikely(revents & POLLHUP)) { + error("POLLFD: LISTENER: processing POLLHUP events for slot %zu (events = %d, revents = %d)", i, pf->events, pf->revents); + poll_close_fd(&p, pi); + continue; + } + + if(unlikely(revents & POLLNVAL)) { + error("POLLFD: LISTENER: processing POLLNVAP events for slot %zu (events = %d, revents = %d)", i, pf->events, revents); + poll_close_fd(&p, pi); + continue; + } + } + } + + pthread_cleanup_pop(1); + debug(D_POLLFD, "POLLFD: LISTENER: cleanup completed"); +} diff --git a/src/socket.h b/src/socket.h index 89c154a61..bb95347ab 100644 --- a/src/socket.h +++ b/src/socket.h @@ -1,14 +1,61 @@ -// -// Created by costa on 24/12/2016. -// - #ifndef NETDATA_SOCKET_H #define NETDATA_SOCKET_H +#ifndef MAX_LISTEN_FDS +#define MAX_LISTEN_FDS 50 +#endif + +typedef struct listen_sockets { + const char *config_section; // the netdata configuration section to read settings from + const char *default_bind_to; // the default bind to configuration string + int default_port; // the default port to use + int backlog; // the default listen backlog to use + + size_t opened; // the number of sockets opened + size_t failed; // the number of sockets attempted to open, but failed + int fds[MAX_LISTEN_FDS]; // the open sockets + char *fds_names[MAX_LISTEN_FDS]; // descriptions for the open sockets + int fds_types[MAX_LISTEN_FDS]; // the socktype for the open sockets +} LISTEN_SOCKETS; + +extern int listen_sockets_setup(LISTEN_SOCKETS *sockets); +extern void listen_sockets_close(LISTEN_SOCKETS *sockets); + 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); +extern int sock_setnonblock(int fd); +extern int sock_delnonblock(int fd); +extern int sock_setreuse(int fd, int reuse); +extern int sock_setreuse_port(int fd, int reuse); +extern int sock_enlarge_in(int fd); +extern int sock_enlarge_out(int fd); + +extern int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *client_port, size_t portsize); + +#ifndef HAVE_ACCEPT4 +extern int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags); + +#ifndef SOCK_NONBLOCK +#define SOCK_NONBLOCK 00004000 +#endif /* #ifndef SOCK_NONBLOCK */ + +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 02000000 +#endif /* #ifndef SOCK_CLOEXEC */ + +#endif /* #ifndef HAVE_ACCEPT4 */ + + +extern void poll_events(LISTEN_SOCKETS *sockets + , void *(*add_callback)(int fd, short int *events) + , void (*del_callback)(int fd, void *data) + , int (*rcv_callback)(int fd, int socktype, void *data, short int *events) + , int (*snd_callback)(int fd, int socktype, void *data, short int *events) + , void *data +); + #endif //NETDATA_SOCKET_H diff --git a/src/statistical.c b/src/statistical.c new file mode 100644 index 000000000..807bc25ea --- /dev/null +++ b/src/statistical.c @@ -0,0 +1,459 @@ +#include "common.h" + +// -------------------------------------------------------------------------------------------------------------------- + +inline long double sum_and_count(long double *series, size_t entries, size_t *count) { + if(unlikely(entries == 0)) { + if(likely(count)) + *count = 0; + + return NAN; + } + + if(unlikely(entries == 1)) { + if(likely(count)) + *count = (isnan(series[0])?0:1); + + return series[0]; + } + + size_t i, c = 0; + long double sum = 0; + + for(i = 0; i < entries ; i++) { + long double value = series[i]; + if(unlikely(isnan(value) || isinf(value))) continue; + c++; + sum += value; + } + + if(likely(count)) + *count = c; + + if(unlikely(c == 0)) + return NAN; + + return sum; +} + +inline long double sum(long double *series, size_t entries) { + return sum_and_count(series, entries, NULL); +} + +inline long double average(long double *series, size_t entries) { + size_t count = 0; + long double sum = sum_and_count(series, entries, &count); + + if(unlikely(count == 0)) + return NAN; + + return sum / (long double)count; +} + +// -------------------------------------------------------------------------------------------------------------------- + +long double moving_average(long double *series, size_t entries, size_t period) { + if(unlikely(period <= 0)) + return 0.0; + + size_t i, count; + long double sum = 0, avg = 0; + long double p[period]; + + for(count = 0; count < period ; count++) + p[count] = 0.0; + + for(i = 0, count = 0; i < entries; i++) { + long double value = series[i]; + if(unlikely(isnan(value) || isinf(value))) continue; + + if(unlikely(count < period)) { + sum += value; + avg = (count == period - 1) ? sum / (long double)period : 0; + } + else { + sum = sum - p[count % period] + value; + avg = sum / (long double)period; + } + + p[count % period] = value; + count++; + } + + return avg; +} + +// -------------------------------------------------------------------------------------------------------------------- + +static int qsort_compare(const void *a, const void *b) { + long double *p1 = (long double *)a, *p2 = (long double *)b; + long double n1 = *p1, n2 = *p2; + + if(unlikely(isnan(n1) || isnan(n2))) { + if(isnan(n1) && !isnan(n2)) return -1; + if(!isnan(n1) && isnan(n2)) return 1; + return 0; + } + if(unlikely(isinf(n1) || isinf(n2))) { + if(!isinf(n1) && isinf(n2)) return -1; + if(isinf(n1) && !isinf(n2)) return 1; + return 0; + } + + if(unlikely(n1 < n2)) return -1; + if(unlikely(n1 > n2)) return 1; + return 0; +} + +inline void sort_series(long double *series, size_t entries) { + qsort(series, entries, sizeof(long double), qsort_compare); +} + +inline long double *copy_series(long double *series, size_t entries) { + long double *copy = mallocz(sizeof(long double) * entries); + memcpy(copy, series, sizeof(long double) * entries); + return copy; +} + +long double median_on_sorted_series(long double *series, size_t entries) { + if(unlikely(entries == 0)) + return NAN; + + if(unlikely(entries == 1)) + return series[0]; + + if(unlikely(entries == 2)) + return (series[0] + series[1]) / 2; + + long double avg; + if(entries % 2 == 0) { + size_t m = entries / 2; + avg = (series[m] + series[m + 1]) / 2; + } + else { + avg = series[entries / 2]; + } + + return avg; +} + +long double median(long double *series, size_t entries) { + if(unlikely(entries == 0)) + return NAN; + + if(unlikely(entries == 1)) + return series[0]; + + if(unlikely(entries == 2)) + return (series[0] + series[1]) / 2; + + long double *copy = copy_series(series, entries); + sort_series(copy, entries); + + long double avg = median_on_sorted_series(copy, entries); + + freez(copy); + return avg; +} + +// -------------------------------------------------------------------------------------------------------------------- + +long double moving_median(long double *series, size_t entries, size_t period) { + if(entries <= period) + return median(series, entries); + + long double *data = copy_series(series, entries); + + size_t i; + for(i = period; i < entries; i++) { + data[i - period] = median(&series[i - period], period); + } + + long double avg = median(data, entries - period); + freez(data); + return avg; +} + +// -------------------------------------------------------------------------------------------------------------------- + +// http://stackoverflow.com/a/15150143/4525767 +long double running_median_estimate(long double *series, size_t entries) { + long double median = 0.0f; + long double average = 0.0f; + size_t i; + + for(i = 0; i < entries ; i++) { + long double value = series[i]; + if(unlikely(isnan(value) || isinf(value))) continue; + + average += ( value - average ) * 0.1f; // rough running average. + median += copysignl( average * 0.01, value - median ); + } + + return median; +} + +// -------------------------------------------------------------------------------------------------------------------- + +long double standard_deviation(long double *series, size_t entries) { + if(unlikely(entries < 1)) + return NAN; + + if(unlikely(entries == 1)) + return series[0]; + + size_t i, count = 0; + long double sum = 0; + + for(i = 0; i < entries ; i++) { + long double value = series[i]; + if(unlikely(isnan(value) || isinf(value))) continue; + + count++; + sum += value; + } + + if(unlikely(count == 0)) + return NAN; + + if(unlikely(count == 1)) + return sum; + + long double average = sum / (long double)count; + + for(i = 0, count = 0, sum = 0; i < entries ; i++) { + long double value = series[i]; + if(unlikely(isnan(value) || isinf(value))) continue; + + count++; + sum += powl(value - average, 2); + } + + if(unlikely(count == 0)) + return NAN; + + if(unlikely(count == 1)) + return average; + + long double variance = sum / (long double)(count - 1); // remove -1 to have a population stddev + + long double stddev = sqrtl(variance); + return stddev; +} + +// -------------------------------------------------------------------------------------------------------------------- + +long double single_exponential_smoothing(long double *series, size_t entries, long double alpha) { + size_t i, count = 0; + long double level = 0, sum = 0; + + if(unlikely(isnan(alpha))) + alpha = 0.3; + + for(i = 0; i < entries ; i++) { + long double value = series[i]; + if(unlikely(isnan(value) || isinf(value))) continue; + count++; + + sum += value; + + long double last_level = level; + level = alpha * value + (1.0 - alpha) * last_level; + } + + return level; +} + +// -------------------------------------------------------------------------------------------------------------------- + +// http://grisha.org/blog/2016/02/16/triple-exponential-smoothing-forecasting-part-ii/ +long double double_exponential_smoothing(long double *series, size_t entries, long double alpha, long double beta, long double *forecast) { + size_t i, count = 0; + long double level = series[0], trend, sum; + + if(unlikely(isnan(alpha))) + alpha = 0.3; + + if(unlikely(isnan(beta))) + beta = 0.05; + + if(likely(entries > 1)) + trend = series[1] - series[0]; + else + trend = 0; + + sum = series[0]; + + for(i = 1; i < entries ; i++) { + long double value = series[i]; + if(unlikely(isnan(value) || isinf(value))) continue; + count++; + + sum += value; + + long double last_level = level; + + level = alpha * value + (1.0 - alpha) * (level + trend); + trend = beta * (level - last_level) + (1.0 - beta) * trend; + } + + if(forecast) + *forecast = level + trend; + + return level; +} + +// -------------------------------------------------------------------------------------------------------------------- + +/* + * Based on th R implementation + * + * a: level component + * b: trend component + * s: seasonal component + * + * Additive: + * + * Yhat[t+h] = a[t] + h * b[t] + s[t + 1 + (h - 1) mod p], + * a[t] = α (Y[t] - s[t-p]) + (1-α) (a[t-1] + b[t-1]) + * b[t] = β (a[t] - a[t-1]) + (1-β) b[t-1] + * s[t] = γ (Y[t] - a[t]) + (1-γ) s[t-p] + * + * Multiplicative: + * + * Yhat[t+h] = (a[t] + h * b[t]) * s[t + 1 + (h - 1) mod p], + * a[t] = α (Y[t] / s[t-p]) + (1-α) (a[t-1] + b[t-1]) + * b[t] = β (a[t] - a[t-1]) + (1-β) b[t-1] + * s[t] = γ (Y[t] / a[t]) + (1-γ) s[t-p] + */ +static int __HoltWinters( + long double *series, + int entries, // start_time + h + + long double alpha, // alpha parameter of Holt-Winters Filter. + long double beta, // beta parameter of Holt-Winters Filter. If set to 0, the function will do exponential smoothing. + long double gamma, // gamma parameter used for the seasonal component. If set to 0, an non-seasonal model is fitted. + + int *seasonal, + int *period, + long double *a, // Start value for level (a[0]). + long double *b, // Start value for trend (b[0]). + long double *s, // Vector of start values for the seasonal component (s_1[0] ... s_p[0]) + + /* return values */ + long double *SSE, // The final sum of squared errors achieved in optimizing + long double *level, // Estimated values for the level component (size entries - t + 2) + long double *trend, // Estimated values for the trend component (size entries - t + 2) + long double *season // Estimated values for the seasonal component (size entries - t + 2) +) +{ + if(unlikely(entries < 4)) + return 0; + + int start_time = 2; + + long double res = 0, xhat = 0, stmp = 0; + int i, i0, s0; + + /* copy start values to the beginning of the vectors */ + level[0] = *a; + if(beta > 0) trend[0] = *b; + if(gamma > 0) memcpy(season, s, *period * sizeof(long double)); + + for(i = start_time - 1; i < entries; i++) { + /* indices for period i */ + i0 = i - start_time + 2; + s0 = i0 + *period - 1; + + /* forecast *for* period i */ + xhat = level[i0 - 1] + (beta > 0 ? trend[i0 - 1] : 0); + stmp = gamma > 0 ? season[s0 - *period] : (*seasonal != 1); + if (*seasonal == 1) + xhat += stmp; + else + xhat *= stmp; + + /* Sum of Squared Errors */ + res = series[i] - xhat; + *SSE += res * res; + + /* estimate of level *in* period i */ + if (*seasonal == 1) + level[i0] = alpha * (series[i] - stmp) + + (1 - alpha) * (level[i0 - 1] + trend[i0 - 1]); + else + level[i0] = alpha * (series[i] / stmp) + + (1 - alpha) * (level[i0 - 1] + trend[i0 - 1]); + + /* estimate of trend *in* period i */ + if (beta > 0) + trend[i0] = beta * (level[i0] - level[i0 - 1]) + + (1 - beta) * trend[i0 - 1]; + + /* estimate of seasonal component *in* period i */ + if (gamma > 0) { + if (*seasonal == 1) + season[s0] = gamma * (series[i] - level[i0]) + + (1 - gamma) * stmp; + else + season[s0] = gamma * (series[i] / level[i0]) + + (1 - gamma) * stmp; + } + } + + return 1; +} + +long double holtwinters(long double *series, size_t entries, long double alpha, long double beta, long double gamma, long double *forecast) { + if(unlikely(isnan(alpha))) + alpha = 0.3; + + if(unlikely(isnan(beta))) + beta = 0.05; + + if(unlikely(isnan(gamma))) + gamma = 0; + + int seasonal = 0; + int period = 0; + long double a0 = series[0]; + long double b0 = 0; + long double s[] = {}; + + long double errors = 0.0; + size_t nb_computations = entries; + long double *estimated_level = callocz(nb_computations, sizeof(long double)); + long double *estimated_trend = callocz(nb_computations, sizeof(long double)); + long double *estimated_season = callocz(nb_computations, sizeof(long double)); + + int ret = __HoltWinters( + series, + (int)entries, + alpha, + beta, + gamma, + &seasonal, + &period, + &a0, + &b0, + s, + &errors, + estimated_level, + estimated_trend, + estimated_season + ); + + long double value = estimated_level[nb_computations - 1]; + + if(forecast) + *forecast = 0.0; + + freez(estimated_level); + freez(estimated_trend); + freez(estimated_season); + + if(!ret) + return 0.0; + + return value; +} diff --git a/src/statistical.h b/src/statistical.h new file mode 100644 index 000000000..844e579bb --- /dev/null +++ b/src/statistical.h @@ -0,0 +1,19 @@ +#ifndef NETDATA_STATISTICAL_H +#define NETDATA_STATISTICAL_H + +extern long double average(long double *series, size_t entries); +extern long double moving_average(long double *series, size_t entries, size_t period); +extern long double median(long double *series, size_t entries); +extern long double moving_median(long double *series, size_t entries, size_t period); +extern long double running_median_estimate(long double *series, size_t entries); +extern long double standard_deviation(long double *series, size_t entries); +extern long double single_exponential_smoothing(long double *series, size_t entries, long double alpha); +extern long double double_exponential_smoothing(long double *series, size_t entries, long double alpha, long double beta, long double *forecast); +extern long double holtwinters(long double *series, size_t entries, long double alpha, long double beta, long double gamma, long double *forecast); +extern long double sum_and_count(long double *series, size_t entries, size_t *count); +extern long double sum(long double *series, size_t entries); +extern long double median_on_sorted_series(long double *series, size_t entries); +extern long double *copy_series(long double *series, size_t entries); +extern void sort_series(long double *series, size_t entries); + +#endif //NETDATA_STATISTICAL_H diff --git a/src/statsd.c b/src/statsd.c new file mode 100644 index 000000000..4dd04757b --- /dev/null +++ b/src/statsd.c @@ -0,0 +1,2041 @@ +#include "common.h" + +#define STATSD_CHART_PREFIX "statsd" +#define STATSD_CHART_PRIORITY 90000 + +// -------------------------------------------------------------------------------------- + +// #define STATSD_MULTITHREADED 1 + +#ifdef STATSD_MULTITHREADED +// DO NOT ENABLE MULTITHREADING - IT IS NOT WELL TESTED +#define STATSD_AVL_TREE avl_tree_lock +#define STATSD_AVL_INSERT avl_insert_lock +#define STATSD_AVL_SEARCH avl_search_lock +#define STATSD_AVL_INDEX_INIT { .avl_tree = { NULL, statsd_metric_compare }, .rwlock = AVL_LOCK_INITIALIZER } +#define STATSD_FIRST_PTR_MUTEX netdata_mutex_t first_mutex +#define STATSD_FIRST_PTR_MUTEX_INIT .first_mutex = NETDATA_MUTEX_INITIALIZER +#define STATSD_FIRST_PTR_MUTEX_LOCK(index) netdata_mutex_lock(&((index)->first_mutex)) +#define STATSD_FIRST_PTR_MUTEX_UNLOCK(index) netdata_mutex_unlock(&((index)->first_mutex)) +#define STATSD_DICTIONARY_OPTIONS DICTIONARY_FLAG_DEFAULT +#else +#define STATSD_AVL_TREE avl_tree +#define STATSD_AVL_INSERT avl_insert +#define STATSD_AVL_SEARCH avl_search +#define STATSD_AVL_INDEX_INIT { .root = NULL, .compar = statsd_metric_compare } +#define STATSD_FIRST_PTR_MUTEX +#define STATSD_FIRST_PTR_MUTEX_INIT +#define STATSD_FIRST_PTR_MUTEX_LOCK(index) +#define STATSD_FIRST_PTR_MUTEX_UNLOCK(index) +#define STATSD_DICTIONARY_OPTIONS DICTIONARY_FLAG_SINGLE_THREADED +#endif + +#define STATSD_DECIMAL_DETAIL 1000 // floating point values get multiplied by this, with the same divisor + +// -------------------------------------------------------------------------------------------------------------------- +// data specific to each metric type + +typedef struct statsd_metric_gauge { + long double value; +} STATSD_METRIC_GAUGE; + +typedef struct statsd_metric_counter { // counter and meter + long long value; +} STATSD_METRIC_COUNTER; + +typedef struct statsd_histogram_extensions { + netdata_mutex_t mutex; + + // average is stored in metric->last + collected_number last_min; + collected_number last_max; + collected_number last_percentile; + collected_number last_median; + collected_number last_stddev; + collected_number last_sum; + + RRDDIM *rd_min; + RRDDIM *rd_max; + RRDDIM *rd_percentile; + RRDDIM *rd_median; + RRDDIM *rd_stddev; + RRDDIM *rd_sum; + + size_t size; + size_t used; + long double *values; // dynamic array of values collected +} STATSD_METRIC_HISTOGRAM_EXTENSIONS; + +typedef struct statsd_metric_histogram { // histogram and timer + STATSD_METRIC_HISTOGRAM_EXTENSIONS *ext; +} STATSD_METRIC_HISTOGRAM; + +typedef struct statsd_metric_set { + DICTIONARY *dict; + size_t unique; +} STATSD_METRIC_SET; + + +// -------------------------------------------------------------------------------------------------------------------- +// this is a metric - for all types of metrics + +typedef enum statsd_metric_options { + STATSD_METRIC_OPTION_NONE = 0x00000000, // no options set + STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED = 0x00000001, // do not update the chart dimension, when this metric is not collected + STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED = 0x00000002, // render a private chart for this metric + STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED = 0x00000004, // the metric has been checked if it should get private chart or not + STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT = 0x00000008, // show the count of events for this private chart + STATSD_METRIC_OPTION_CHECKED_IN_APPS = 0x00000010, // set when this metric has been checked agains apps +} STATS_METRIC_OPTIONS; + +typedef enum statsd_metric_type { + STATSD_METRIC_TYPE_GAUGE, + STATSD_METRIC_TYPE_COUNTER, + STATSD_METRIC_TYPE_METER, + STATSD_METRIC_TYPE_TIMER, + STATSD_METRIC_TYPE_HISTOGRAM, + STATSD_METRIC_TYPE_SET +} STATSD_METRIC_TYPE; + + +typedef struct statsd_metric { + avl avl; // indexing + + const char *name; // the name of the metric + uint32_t hash; // hash of the name + + STATSD_METRIC_TYPE type; + + // metadata about data collection + collected_number events; // the number of times this metric has been collected (never resets) + size_t count; // the number of times this metric has been collected since the last flush + + // the actual collected data + union { + STATSD_METRIC_GAUGE gauge; + STATSD_METRIC_COUNTER counter; + STATSD_METRIC_HISTOGRAM histogram; + STATSD_METRIC_SET set; + }; + + // chart related members + STATS_METRIC_OPTIONS options; // STATSD_METRIC_OPTION_* (bitfield) + char reset; // set to 1 to reset this metric to zero + collected_number last; // the last value sent to netdata + RRDSET *st; // the chart of this metric + RRDDIM *rd_value; // the dimension of this metric value + RRDDIM *rd_count; // the dimension for the number of events received + + // linking, used for walking through all metrics + struct statsd_metric *next; +} STATSD_METRIC; + + +// -------------------------------------------------------------------------------------------------------------------- +// each type of metric has its own index + +typedef struct statsd_index { + char *name; // the name of the index of metrics + size_t events; // the number of events processed for this index + size_t metrics; // the number of metrics in this index + + STATSD_AVL_TREE index; // the AVL tree + + STATSD_METRIC *first; // the linked list of metrics (new metrics are added in front) + STATSD_FIRST_PTR_MUTEX; // when mutli-threading is enabled, a lock to protect the linked list + + STATS_METRIC_OPTIONS default_options; // default options for all metrics in this index +} STATSD_INDEX; + +static int statsd_metric_compare(void* a, void* b); + +// -------------------------------------------------------------------------------------------------------------------- +// synthetic charts + +typedef enum statsd_app_chart_dimension_value_type { + STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS, + STATSD_APP_CHART_DIM_VALUE_TYPE_LAST, + STATSD_APP_CHART_DIM_VALUE_TYPE_AVERAGE, + STATSD_APP_CHART_DIM_VALUE_TYPE_SUM, + STATSD_APP_CHART_DIM_VALUE_TYPE_MIN, + STATSD_APP_CHART_DIM_VALUE_TYPE_MAX, + STATSD_APP_CHART_DIM_VALUE_TYPE_PERCENTILE, + STATSD_APP_CHART_DIM_VALUE_TYPE_MEDIAN, + STATSD_APP_CHART_DIM_VALUE_TYPE_STDDEV +} STATSD_APP_CHART_DIM_VALUE_TYPE; + +typedef struct statsd_app_chart_dimension { + const char *name; + const char *metric; + uint32_t metric_hash; + collected_number multiplier; + collected_number divisor; + STATSD_APP_CHART_DIM_VALUE_TYPE value_type; + + RRDDIM *rd; + collected_number *value_ptr; + RRD_ALGORITHM algorithm; + + struct statsd_app_chart_dimension *next; +} STATSD_APP_CHART_DIM; + +typedef struct statsd_app_chart { + const char *source; + const char *id; + const char *name; + const char *title; + const char *family; + const char *context; + const char *units; + long priority; + RRDSET_TYPE chart_type; + STATSD_APP_CHART_DIM *dimensions; + size_t dimensions_count; + size_t dimensions_linked_count; + + RRDSET *st; + struct statsd_app_chart *next; +} STATSD_APP_CHART; + +typedef struct statsd_app { + const char *name; + SIMPLE_PATTERN *metrics; + STATS_METRIC_OPTIONS default_options; + RRD_MEMORY_MODE rrd_memory_mode; + long rrd_history_entries; + + const char *source; + STATSD_APP_CHART *charts; + struct statsd_app *next; +} STATSD_APP; + +// -------------------------------------------------------------------------------------------------------------------- +// global statsd data + +static struct statsd { + STATSD_INDEX gauges; + STATSD_INDEX counters; + STATSD_INDEX timers; + STATSD_INDEX histograms; + STATSD_INDEX meters; + STATSD_INDEX sets; + size_t unknown_types; + size_t socket_errors; + size_t tcp_socket_reads; + size_t tcp_packets_received; + size_t tcp_bytes_read; + size_t udp_socket_reads; + size_t udp_packets_received; + size_t udp_bytes_read; + + int enabled; + int update_every; + SIMPLE_PATTERN *charts_for; + + size_t private_charts; + size_t max_private_charts; + size_t max_private_charts_hard; + RRD_MEMORY_MODE private_charts_memory_mode; + long private_charts_rrd_history_entries; + + STATSD_APP *apps; + size_t recvmmsg_size; + size_t histogram_increase_step; + double histogram_percentile; + char *histogram_percentile_str; + int threads; + LISTEN_SOCKETS sockets; +} statsd = { + .enabled = 1, + .max_private_charts = 200, + .max_private_charts_hard = 1000, + .recvmmsg_size = 10, + + .gauges = { + .name = "gauge", + .events = 0, + .metrics = 0, + .index = STATSD_AVL_INDEX_INIT, + .default_options = STATSD_METRIC_OPTION_NONE, + .first = NULL, + STATSD_FIRST_PTR_MUTEX_INIT + }, + .counters = { + .name = "counter", + .events = 0, + .metrics = 0, + .index = STATSD_AVL_INDEX_INIT, + .default_options = STATSD_METRIC_OPTION_NONE, + .first = NULL, + STATSD_FIRST_PTR_MUTEX_INIT + }, + .timers = { + .name = "timer", + .events = 0, + .metrics = 0, + .index = STATSD_AVL_INDEX_INIT, + .default_options = STATSD_METRIC_OPTION_NONE, + .first = NULL, + STATSD_FIRST_PTR_MUTEX_INIT + }, + .histograms = { + .name = "histogram", + .events = 0, + .metrics = 0, + .index = STATSD_AVL_INDEX_INIT, + .default_options = STATSD_METRIC_OPTION_NONE, + .first = NULL, + STATSD_FIRST_PTR_MUTEX_INIT + }, + .meters = { + .name = "meter", + .events = 0, + .metrics = 0, + .index = STATSD_AVL_INDEX_INIT, + .default_options = STATSD_METRIC_OPTION_NONE, + .first = NULL, + STATSD_FIRST_PTR_MUTEX_INIT + }, + .sets = { + .name = "set", + .events = 0, + .metrics = 0, + .index = STATSD_AVL_INDEX_INIT, + .default_options = STATSD_METRIC_OPTION_NONE, + .first = NULL, + STATSD_FIRST_PTR_MUTEX_INIT + }, + + .apps = NULL, + .histogram_percentile = 95.0, + .histogram_increase_step = 10, + .threads = 0, + .sockets = { + .config_section = CONFIG_SECTION_STATSD, + .default_bind_to = "udp:localhost tcp:localhost", + .default_port = STATSD_LISTEN_PORT, + .backlog = STATSD_LISTEN_BACKLOG + }, +}; + + +// -------------------------------------------------------------------------------------------------------------------- +// statsd index management - add/find metrics + +static int statsd_metric_compare(void* a, void* b) { + if(((STATSD_METRIC *)a)->hash < ((STATSD_METRIC *)b)->hash) return -1; + else if(((STATSD_METRIC *)a)->hash > ((STATSD_METRIC *)b)->hash) return 1; + else return strcmp(((STATSD_METRIC *)a)->name, ((STATSD_METRIC *)b)->name); +} + +static inline STATSD_METRIC *stasd_metric_index_find(STATSD_INDEX *index, const char *name, uint32_t hash) { + STATSD_METRIC tmp; + tmp.name = name; + tmp.hash = (hash)?hash:simple_hash(tmp.name); + + return (STATSD_METRIC *)STATSD_AVL_SEARCH(&index->index, (avl *)&tmp); +} + +static inline STATSD_METRIC *statsd_find_or_add_metric(STATSD_INDEX *index, const char *name, STATSD_METRIC_TYPE type) { + debug(D_STATSD, "searching for metric '%s' under '%s'", name, index->name); + + uint32_t hash = simple_hash(name); + + STATSD_METRIC *m = stasd_metric_index_find(index, name, hash); + if(unlikely(!m)) { + debug(D_STATSD, "Creating new %s metric '%s'", index->name, name); + + m = (STATSD_METRIC *)callocz(sizeof(STATSD_METRIC), 1); + m->name = strdupz(name); + m->hash = hash; + m->type = type; + m->options = index->default_options; + + if(type == STATSD_METRIC_TYPE_HISTOGRAM || type == STATSD_METRIC_TYPE_TIMER) { + m->histogram.ext = callocz(sizeof(STATSD_METRIC_HISTOGRAM_EXTENSIONS), 1); + netdata_mutex_init(&m->histogram.ext->mutex); + } + STATSD_METRIC *n = (STATSD_METRIC *)STATSD_AVL_INSERT(&index->index, (avl *)m); + if(unlikely(n != m)) { + freez((void *)m->histogram.ext); + freez((void *)m->name); + freez((void *)m); + m = n; + } + else { + STATSD_FIRST_PTR_MUTEX_LOCK(index); + index->metrics++; + m->next = index->first; + index->first = m; + STATSD_FIRST_PTR_MUTEX_UNLOCK(index); + } + } + + index->events++; + return m; +} + + +// -------------------------------------------------------------------------------------------------------------------- +// statsd parsing numbers + +static inline long double statsd_parse_float(const char *v, long double def) { + long double value; + + if(likely(v && *v)) { + char *e = NULL; + value = str2ld(v, &e); + if(unlikely(e && *e)) + error("STATSD: excess data '%s' after value '%s'", e, v); + } + else + value = def; + + return value; +} + +static inline long long statsd_parse_int(const char *v, long long def) { + long long value; + + if(likely(v && *v)) { + char *e = NULL; + value = str2ll(v, &e); + if(unlikely(e && *e)) + error("STATSD: excess data '%s' after value '%s'", e, v); + } + else + value = def; + + return value; +} + + +// -------------------------------------------------------------------------------------------------------------------- +// statsd processors per metric type + +static inline void statsd_reset_metric(STATSD_METRIC *m) { + m->reset = 0; + m->count = 0; +} + +static inline void statsd_process_gauge(STATSD_METRIC *m, const char *value, const char *sampling) { + if(unlikely(!value || !*value)) { + error("STATSD: metric '%s' of type gauge, with empty value is ignored.", m->name); + return; + } + + if(unlikely(m->reset)) { + // no need to reset anything specific for gauges + statsd_reset_metric(m); + } + + if(unlikely(*value == '+' || *value == '-')) + m->gauge.value += statsd_parse_float(value, 1.0) / statsd_parse_float(sampling, 1.0); + else + m->gauge.value = statsd_parse_float(value, 1.0) / statsd_parse_float(sampling, 1.0); + + m->events++; + m->count++; +} + +static inline void statsd_process_counter(STATSD_METRIC *m, const char *value, const char *sampling) { + // we accept empty values for counters + + if(unlikely(m->reset)) statsd_reset_metric(m); + + m->counter.value += roundl((long double)statsd_parse_int(value, 1) / statsd_parse_float(sampling, 1.0)); + + m->events++; + m->count++; +} + +static inline void statsd_process_meter(STATSD_METRIC *m, const char *value, const char *sampling) { + // this is the same with the counter + statsd_process_counter(m, value, sampling); +} + +static inline void statsd_process_histogram(STATSD_METRIC *m, const char *value, const char *sampling) { + if(unlikely(!value || !*value)) { + error("STATSD: metric '%s' of type histogram, with empty value is ignored.", m->name); + return; + } + + if(unlikely(m->reset)) { + m->histogram.ext->used = 0; + statsd_reset_metric(m); + } + + if(unlikely(m->histogram.ext->used == m->histogram.ext->size)) { + netdata_mutex_lock(&m->histogram.ext->mutex); + m->histogram.ext->size += statsd.histogram_increase_step; + m->histogram.ext->values = reallocz(m->histogram.ext->values, sizeof(long double) * m->histogram.ext->size); + netdata_mutex_unlock(&m->histogram.ext->mutex); + } + + m->histogram.ext->values[m->histogram.ext->used++] = statsd_parse_float(value, 1.0) / statsd_parse_float(sampling, 1.0); + + m->events++; + m->count++; +} + +static inline void statsd_process_timer(STATSD_METRIC *m, const char *value, const char *sampling) { + if(unlikely(!value || !*value)) { + error("STATSD: metric of type set, with empty value is ignored."); + return; + } + + // timers are a use case of histogram + statsd_process_histogram(m, value, sampling); +} + +static inline void statsd_process_set(STATSD_METRIC *m, const char *value) { + if(unlikely(!value || !*value)) { + error("STATSD: metric of type set, with empty value is ignored."); + return; + } + + if(unlikely(m->reset)) { + if(likely(m->set.dict)) { + dictionary_destroy(m->set.dict); + m->set.dict = NULL; + } + statsd_reset_metric(m); + } + + if(unlikely(!m->set.dict)) { + m->set.dict = dictionary_create(STATSD_DICTIONARY_OPTIONS|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE); + m->set.unique = 0; + } + + void *t = dictionary_get(m->set.dict, value); + if(unlikely(!t)) { + dictionary_set(m->set.dict, value, NULL, 1); + m->set.unique++; + } + + m->events++; + m->count++; +} + + +// -------------------------------------------------------------------------------------------------------------------- +// statsd parsing + +static void statsd_process_metric(const char *name, const char *value, const char *type, const char *sampling) { + debug(D_STATSD, "STATSD: raw metric '%s', value '%s', type '%s', rate '%s'", name?name:"(null)", value?value:"(null)", type?type:"(null)", sampling?sampling:"(null)"); + + if(unlikely(!name || !*name)) return; + if(unlikely(!type || !*type)) type = "m"; + + char t0 = type[0], t1 = type[1]; + + if(unlikely(t0 == 'g' && t1 == '\0')) { + statsd_process_gauge( + statsd_find_or_add_metric(&statsd.gauges, name, STATSD_METRIC_TYPE_GAUGE), + value, sampling); + } + else if(unlikely((t0 == 'c' || t0 == 'C') && t1 == '\0')) { + // etsy/statsd uses 'c' + // brubeck uses 'C' + statsd_process_counter( + statsd_find_or_add_metric(&statsd.counters, name, STATSD_METRIC_TYPE_COUNTER), + value, sampling); + } + else if(unlikely(t0 == 'm' && t1 == '\0')) { + statsd_process_meter( + statsd_find_or_add_metric(&statsd.meters, name, STATSD_METRIC_TYPE_METER), + value, sampling); + } + else if(unlikely(t0 == 'h' && t1 == '\0')) { + statsd_process_histogram( + statsd_find_or_add_metric(&statsd.histograms, name, STATSD_METRIC_TYPE_HISTOGRAM), + value, sampling); + } + else if(unlikely(t0 == 's' && t1 == '\0')) { + statsd_process_set( + statsd_find_or_add_metric(&statsd.sets, name, STATSD_METRIC_TYPE_SET), + value); + } + else if(unlikely(t0 == 'm' && t1 == 's' && type[2] == '\0')) { + statsd_process_timer( + statsd_find_or_add_metric(&statsd.timers, name, STATSD_METRIC_TYPE_TIMER), + value, sampling); + } + else { + statsd.unknown_types++; + error("STATSD: metric '%s' with value '%s' is sent with unknown metric type '%s'", name, value?value:"", type); + } +} + +static inline const char *statsd_parse_skip_up_to(const char *s, char d1, char d2) { + char c; + + for(c = *s; c && c != d1 && c != d2 && c != '\r' && c != '\n'; c = *++s) ; + + return s; +} + +const char *statsd_parse_skip_spaces(const char *s) { + char c; + + for(c = *s; c && ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ); c = *++s) ; + + return s; +} + +static inline const char *statsd_parse_field_trim(const char *start, char *end) { + if(unlikely(!start)) { + start = end; + return start; + } + + while(start <= end && (*start == ' ' || *start == '\t')) + start++; + + *end = '\0'; + end--; + while(end >= start && (*end == ' ' || *end == '\t')) + *end-- = '\0'; + + return start; +} + +static inline size_t statsd_process(char *buffer, size_t size, int require_newlines) { + buffer[size] = '\0'; + debug(D_STATSD, "RECEIVED: %zu bytes: '%s'", size, buffer); + + const char *s = buffer; + while(*s) { + const char *name = NULL, *value = NULL, *type = NULL, *sampling = NULL; + char *name_end = NULL, *value_end = NULL, *type_end = NULL, *sampling_end = NULL; + + s = name_end = (char *)statsd_parse_skip_up_to(name = s, ':', '|'); + if(name == name_end) { + s = statsd_parse_skip_spaces(s); + continue; + } + + if(likely(*s == ':')) + s = value_end = (char *) statsd_parse_skip_up_to(value = ++s, '|', '|'); + + if(likely(*s == '|')) + s = type_end = (char *) statsd_parse_skip_up_to(type = ++s, '|', '@'); + + if(likely(*s == '|' || *s == '@')) { + s = sampling_end = (char *) statsd_parse_skip_up_to(sampling = ++s, '\r', '\n'); + if(*sampling == '@') sampling++; + } + + // skip everything until the end of the line + while(*s && *s != '\n') s++; + + if(unlikely(require_newlines && *s != '\n' && s > buffer)) { + // move the remaining data to the beginning + size -= (name - buffer); + memmove(buffer, name, size); + return size; + } + else + s = statsd_parse_skip_spaces(s); + + statsd_process_metric( + statsd_parse_field_trim(name, name_end) + , statsd_parse_field_trim(value, value_end) + , statsd_parse_field_trim(type, type_end) + , statsd_parse_field_trim(sampling, sampling_end) + ); + } + + return 0; +} + + +// -------------------------------------------------------------------------------------------------------------------- +// statsd pollfd interface + +#define STATSD_TCP_BUFFER_SIZE 65536 // minimize tcp reads +#define STATSD_UDP_BUFFER_SIZE 9000 // this should be up to MTU + +typedef enum { + STATSD_SOCKET_DATA_TYPE_TCP, + STATSD_SOCKET_DATA_TYPE_UDP +} STATSD_SOCKET_DATA_TYPE; + +struct statsd_tcp { + STATSD_SOCKET_DATA_TYPE type; + size_t size; + size_t len; + char buffer[]; +}; + +#ifdef HAVE_RECVMMSG +struct statsd_udp { + STATSD_SOCKET_DATA_TYPE type; + size_t size; + struct iovec *iovecs; + struct mmsghdr *msgs; +}; +#else +struct statsd_udp { + STATSD_SOCKET_DATA_TYPE type; + char buffer[STATSD_UDP_BUFFER_SIZE]; +}; +#endif + +// new TCP client connected +static void *statsd_add_callback(int fd, short int *events) { + (void)fd; + *events = POLLIN; + + struct statsd_tcp *data = (struct statsd_tcp *)callocz(sizeof(struct statsd_tcp) + STATSD_TCP_BUFFER_SIZE, 1); + data->type = STATSD_SOCKET_DATA_TYPE_TCP; + data->size = STATSD_TCP_BUFFER_SIZE - 1; + + return data; +} + +// TCP client disconnected +static void statsd_del_callback(int fd, void *data) { + (void)fd; + + if(data) { + struct statsd_tcp *t = data; + if(t->type == STATSD_SOCKET_DATA_TYPE_TCP) { + if(t->len != 0) { + statsd.socket_errors++; + error("STATSD: client is probably sending unterminated metrics. Closed socket left with '%s'. Trying to process it.", t->buffer); + statsd_process(t->buffer, t->len, 0); + } + } + else + error("STATSD: internal error: received socket data type is %d, but expected %d", (int)t->type, (int)STATSD_SOCKET_DATA_TYPE_TCP); + + freez(data); + } + + return; +} + +// Receive data +static int statsd_rcv_callback(int fd, int socktype, void *data, short int *events) { + *events = POLLIN; + + switch(socktype) { + case SOCK_STREAM: { + struct statsd_tcp *d = (struct statsd_tcp *)data; + if(unlikely(!d)) { + error("STATSD: internal error: expected TCP data pointer is NULL"); + statsd.socket_errors++; + return -1; + } + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(d->type != STATSD_SOCKET_DATA_TYPE_TCP)) { + error("STATSD: internal error: socket data type should be %d, but it is %d", (int)STATSD_SOCKET_DATA_TYPE_TCP, (int)d->type); + statsd.socket_errors++; + return -1; + } +#endif + + int ret = 0; + ssize_t rc; + do { + rc = recv(fd, &d->buffer[d->len], d->size - d->len, MSG_DONTWAIT); + if (rc < 0) { + // read failed + if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) { + error("STATSD: recv() on TCP socket %d failed.", fd); + statsd.socket_errors++; + ret = -1; + } + } + else if (!rc) { + // connection closed + debug(D_STATSD, "STATSD: client disconnected."); + ret = -1; + } + else { + // data received + d->len += rc; + statsd.tcp_socket_reads++; + statsd.tcp_bytes_read += rc; + } + + if(likely(d->len > 0)) { + statsd.tcp_packets_received++; + d->len = statsd_process(d->buffer, d->len, 1); + } + + if(unlikely(ret == -1)) + return -1; + + } while (rc != -1); + break; + } + + case SOCK_DGRAM: { + struct statsd_udp *d = (struct statsd_udp *)data; + if(unlikely(!d)) { + error("STATSD: internal error: expected UDP data pointer is NULL"); + statsd.socket_errors++; + return -1; + } + +#ifdef NETDATA_INTERNAL_CHECKS + if(unlikely(d->type != STATSD_SOCKET_DATA_TYPE_UDP)) { + error("STATSD: internal error: socket data should be %d, but it is %d", (int)d->type, (int)STATSD_SOCKET_DATA_TYPE_UDP); + statsd.socket_errors++; + return -1; + } +#endif + +#ifdef HAVE_RECVMMSG + ssize_t rc; + do { + rc = recvmmsg(fd, d->msgs, (unsigned int)d->size, MSG_DONTWAIT, NULL); + if (rc < 0) { + // read failed + if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) { + error("STATSD: recvmmsg() on UDP socket %d failed.", fd); + statsd.socket_errors++; + return -1; + } + } else if (rc) { + // data received + statsd.udp_socket_reads++; + statsd.udp_packets_received += rc; + + size_t i; + for (i = 0; i < (size_t)rc; ++i) { + size_t len = (size_t)d->msgs[i].msg_len; + statsd.udp_bytes_read += len; + statsd_process(d->msgs[i].msg_hdr.msg_iov->iov_base, len, 0); + } + } + } while (rc != -1); + +#else // !HAVE_RECVMMSG + ssize_t rc; + do { + rc = recv(fd, d->buffer, STATSD_UDP_BUFFER_SIZE - 1, MSG_DONTWAIT); + if (rc < 0) { + // read failed + if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) { + error("STATSD: recv() on UDP socket %d failed.", fd); + statsd.socket_errors++; + return -1; + } + } else if (rc) { + // data received + statsd.udp_socket_reads++; + statsd.udp_packets_received++; + statsd.udp_bytes_read += rc; + statsd_process(d->buffer, (size_t) rc, 0); + } + } while (rc != -1); +#endif + + break; + } + + default: { + error("STATSD: internal error: unknown socktype %d on socket %d", socktype, fd); + statsd.socket_errors++; + return -1; + } + } + + return 0; +} + +static int statsd_snd_callback(int fd, int socktype, void *data, short int *events) { + (void)fd; + (void)socktype; + (void)data; + (void)events; + + error("STATSD: snd_callback() called, but we never requested to send data to statsd clients."); + return -1; +} + +// -------------------------------------------------------------------------------------------------------------------- +// statsd child thread to collect metrics from network + +void statsd_collector_thread_cleanup(void *data) { + struct statsd_udp *d = data; + +#ifdef HAVE_RECVMMSG + size_t i; + for (i = 0; i < d->size; i++) + freez(d->iovecs[i].iov_base); + + freez(d->iovecs); + freez(d->msgs); +#endif + + freez(d); +} + +void *statsd_collector_thread(void *ptr) { + int id = *((int *)ptr); + + info("STATSD collector thread No %d created with task id %d", id + 1, gettid()); + + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("Cannot set pthread cancel type to DEFERRED."); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("Cannot set pthread cancel state to ENABLE."); + + struct statsd_udp *d = callocz(sizeof(struct statsd_udp), 1); + pthread_cleanup_push(statsd_collector_thread_cleanup, d); + +#ifdef HAVE_RECVMMSG + d->type = STATSD_SOCKET_DATA_TYPE_UDP; + d->size = statsd.recvmmsg_size; + d->iovecs = callocz(sizeof(struct iovec), d->size); + d->msgs = callocz(sizeof(struct mmsghdr), d->size); + + size_t i; + for (i = 0; i < d->size; i++) { + d->iovecs[i].iov_base = mallocz(STATSD_UDP_BUFFER_SIZE); + d->iovecs[i].iov_len = STATSD_UDP_BUFFER_SIZE - 1; + d->msgs[i].msg_hdr.msg_iov = &d->iovecs[i]; + d->msgs[i].msg_hdr.msg_iovlen = 1; + } +#endif + + poll_events(&statsd.sockets + , statsd_add_callback + , statsd_del_callback + , statsd_rcv_callback + , statsd_snd_callback + , (void *)d + ); + + pthread_cleanup_pop(1); + + debug(D_WEB_CLIENT, "STATSD: exit!"); + pthread_exit(NULL); + return NULL; +} + + +// -------------------------------------------------------------------------------------------------------------------- +// statsd applications configuration files parsing + +#define STATSD_CONF_LINE_MAX 8192 + +int statsd_readfile(const char *path, const char *filename) { + debug(D_STATSD, "STATSD configuration reading file '%s/%s'", path, filename); + + char buffer[STATSD_CONF_LINE_MAX + 1]; + + FILE *fp = NULL; + snprintfz(buffer, STATSD_CONF_LINE_MAX, "%s/%s", path, filename); + fp = fopen(buffer, "r"); + if(!fp) { + error("STATSD: cannot open file '%s'.", buffer); + return -1; + } + + STATSD_APP *app = NULL; + STATSD_APP_CHART *chart = NULL; + + size_t line = 0; + char *s; + while(fgets(buffer, STATSD_CONF_LINE_MAX, fp) != NULL) { + buffer[STATSD_CONF_LINE_MAX] = '\0'; + line++; + + s = trim(buffer); + if (!s || *s == '#') { + debug(D_STATSD, "STATSD: ignoring line %zu of file '%s/%s', it is empty.", line, path, filename); + continue; + } + debug(D_STATSD, "STATSD: processing line %zu of file '%s/%s': %s", line, path, filename, buffer); + + int len = (int) strlen(s); + if (*s == '[' && s[len - 1] == ']') { + // new section + s[len - 1] = '\0'; + s++; + + if (!strcmp(s, "app")) { + // a new app + app = callocz(sizeof(STATSD_APP), 1); + app->name = strdupz("unnamed"); + app->rrd_memory_mode = localhost->rrd_memory_mode; + app->rrd_history_entries = localhost->rrd_history_entries; + + app->next = statsd.apps; + statsd.apps = app; + chart = NULL; + } + else if(app) { + // a new chart + chart = callocz(sizeof(STATSD_APP_CHART), 1); + netdata_fix_chart_id(s); + chart->id = strdupz(s); + chart->name = strdupz(s); + chart->title = strdupz("Statsd chart"); + chart->context = strdupz(s); + chart->family = strdupz("overview"); + chart->units = strdupz("value"); + chart->priority = STATSD_CHART_PRIORITY; + chart->chart_type = RRDSET_TYPE_LINE; + + chart->next = app->charts; + app->charts = chart; + } + else + error("STATSD: ignoring line %zu ('%s') of file '%s/%s', [app] is not defined.", line, s, path, filename); + + continue; + } + + if(!app) { + error("STATSD: ignoring line %zu ('%s') of file '%s/%s', it is outside all sections.", line, s, path, filename); + continue; + } + + char *name = s; + char *value = strchr(s, '='); + if(!value) { + error("STATSD: ignoring line %zu ('%s') of file '%s/%s', there is no = in it.", line, s, path, filename); + continue; + } + *value = '\0'; + value++; + + name = trim(name); + value = trim(value); + + if(!name || *name == '#') { + error("STATSD: ignoring line %zu of file '%s/%s', name is empty.", line, path, filename); + continue; + } + if(!value) { + debug(D_CONFIG, "STATSD: ignoring line %zu of file '%s/%s', value is empty.", line, path, filename); + continue; + } + + if(!chart) { + if(!strcmp(name, "name")) { + freez((void *)app->name); + netdata_fix_chart_name(value); + app->name = strdupz(value); + } + else if (!strcmp(name, "metrics")) { + simple_pattern_free(app->metrics); + app->metrics = simple_pattern_create(value, SIMPLE_PATTERN_EXACT); + } + else if (!strcmp(name, "private charts")) { + if (!strcmp(value, "yes") || !strcmp(value, "on")) + app->default_options |= STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED; + else + app->default_options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED; + } + else if (!strcmp(name, "gaps when not collected")) { + if (!strcmp(value, "yes") || !strcmp(value, "on")) + app->default_options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; + } + else if (!strcmp(name, "memory mode")) { + app->rrd_memory_mode = rrd_memory_mode_id(value); + } + else if (!strcmp(name, "history")) { + app->rrd_history_entries = atol(value); + if (app->rrd_history_entries < 5) + app->rrd_history_entries = 5; + } + else { + error("STATSD: ignoring line %zu ('%s') of file '%s/%s'. Unknown keyword for the [app] section.", line, name, path, filename); + continue; + } + } + else { + if(!strcmp(name, "name")) { + freez((void *)chart->name); + netdata_fix_chart_id(value); + chart->name = strdupz(value); + } + else if(!strcmp(name, "title")) { + freez((void *)chart->title); + chart->title = strdupz(value); + } + else if (!strcmp(name, "family")) { + freez((void *)chart->family); + chart->family = strdupz(value); + } + else if (!strcmp(name, "context")) { + freez((void *)chart->context); + netdata_fix_chart_id(value); + chart->context = strdupz(value); + } + else if (!strcmp(name, "units")) { + freez((void *)chart->units); + chart->units = strdupz(value); + } + else if (!strcmp(name, "priority")) { + chart->priority = atol(value); + } + else if (!strcmp(name, "type")) { + chart->chart_type = rrdset_type_id(value); + } + else if (!strcmp(name, "dimension")) { + // metric [name [type [multiplier [divisor]]]] + char *words[5]; + pluginsd_split_words(value, words, 5); + + char *metric_name = words[0]; + char *dim_name = words[1]; + char *type = words[2]; + char *multipler = words[3]; + char *divisor = words[4]; + + STATSD_APP_CHART_DIM *dim = callocz(sizeof(STATSD_APP_CHART_DIM), 1); + + dim->metric = strdupz(metric_name); + dim->metric_hash = simple_hash(dim->metric); + + dim->name = strdupz((dim_name && *dim_name)?dim_name:metric_name); + dim->multiplier = (multipler && *multipler)?str2l(multipler):1; + dim->divisor = (divisor && *divisor)?str2l(divisor):1; + + if(!type || !*type) type = "last"; + if(!strcmp(type, "events")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS; + else if(!strcmp(type, "last")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_LAST; + else if(!strcmp(type, "min")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_MIN; + else if(!strcmp(type, "max")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_MAX; + else if(!strcmp(type, "sum")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_SUM; + else if(!strcmp(type, "average")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_AVERAGE; + else if(!strcmp(type, "median")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_MEDIAN; + else if(!strcmp(type, "stddev")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_STDDEV; + else if(!strcmp(type, "percentile")) dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_PERCENTILE; + else { + error("STATSD: invalid type '%s' at line %zu of file '%s/%s'. Using 'last'.", type, line, path, filename); + dim->value_type = STATSD_APP_CHART_DIM_VALUE_TYPE_LAST; + } + + if(!dim->multiplier) { + error("STATSD: invalid multiplier value '%s' at line %zu of file '%s/%s'. Using 1.", multipler, line, path, filename); + dim->multiplier = 1; + } + if(!dim->divisor) { + error("STATSD: invalid divisor value '%s' at line %zu of file '%s/%s'. Using 1.", divisor, line, path, filename); + dim->divisor = 1; + } + + // append it to the list of dimension + STATSD_APP_CHART_DIM *tdim; + for(tdim = chart->dimensions; tdim && tdim->next ; tdim = tdim->next) ; + if(!tdim) { + dim->next = chart->dimensions; + chart->dimensions = dim; + } + else { + dim->next = tdim->next; + tdim->next = dim; + } + chart->dimensions_count++; + + debug(D_STATSD, "Added dimension '%s' to chart '%s' of app '%s', for metric '%s', with type %u, multiplier " COLLECTED_NUMBER_FORMAT ", divisor " COLLECTED_NUMBER_FORMAT, + dim->name, chart->id, app->name, dim->metric, dim->value_type, dim->multiplier, dim->divisor); + } + else { + error("STATSD: ignoring line %zu ('%s') of file '%s/%s'. Unknown keyword for the [%s] section.", line, name, path, filename, chart->id); + continue; + } + } + } + + fclose(fp); + return 0; +} + +static void statsd_readdir(const char *path) { + size_t pathlen = strlen(path); + + debug(D_STATSD, "STATSD configuration reading directory '%s'", path); + + DIR *dir = opendir(path); + if (!dir) { + error("STATSD 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_STATSD, "STATSD: 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); + statsd_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")) { + statsd_readfile(path, de->d_name); + } + + else debug(D_STATSD, "STATSD: ignoring file '%s'", de->d_name); + } + + closedir(dir); +} + +// -------------------------------------------------------------------------------------------------------------------- +// send metrics to netdata - in private charts - called from the main thread + +// extract chart type and chart id from metric name +static inline void statsd_get_metric_type_and_id(STATSD_METRIC *m, char *type, char *id, const char *defid, size_t len) { + char *s; + + snprintfz(type, len, "%s_%s_%s", STATSD_CHART_PREFIX, defid, m->name); + for(s = type; *s ;s++) + if(unlikely(*s == '.')) break; + + if(*s == '.') { + *s++ = '\0'; + strncpyz(id, s, len); + } + else { + strncpyz(id, defid, len); + } + + netdata_fix_chart_id(type); + netdata_fix_chart_id(id); +} + +static inline RRDSET *statsd_private_rrdset_create( + STATSD_METRIC *m + , 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 +) { + RRD_MEMORY_MODE memory_mode = statsd.private_charts_memory_mode; + long history = statsd.private_charts_rrd_history_entries; + + if(unlikely(statsd.private_charts >= statsd.max_private_charts)) { + debug(D_STATSD, "STATSD: metric '%s' will be charted with memory mode = none, because the maximum number of charts has been reached.", m->name); + info("STATSD: metric '%s' will be charted with memory mode = none, because the maximum number of charts (%zu) has been reached. Increase the number of charts by editing netdata.conf, [statsd] section.", m->name, statsd.max_private_charts); + memory_mode = RRD_MEMORY_MODE_NONE; + history = 5; + } + + statsd.private_charts++; + RRDSET *st = rrdset_create_custom( + localhost + , type + , id + , name + , family + , context + , title + , units + , priority + , update_every + , chart_type + , memory_mode + , history + ); + rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST); + // rrdset_flag_set(st, RRDSET_FLAG_DEBUG); + return st; +} + +static inline void statsd_private_chart_gauge(STATSD_METRIC *m) { + debug(D_STATSD, "updating private chart for gauge metric '%s'", m->name); + + if(unlikely(!m->st)) { + char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1]; + statsd_get_metric_type_and_id(m, type, id, "gauge", RRD_ID_LENGTH_MAX); + + m->st = statsd_private_rrdset_create( + m + , type + , id + , NULL // name + , "gauges" // family (submenu) + , m->name // context + , m->name // title + , "value" // units + , STATSD_CHART_PRIORITY + , statsd.update_every + , RRDSET_TYPE_LINE + ); + + m->rd_value = rrddim_add(m->st, "gauge", NULL, 1, STATSD_DECIMAL_DETAIL, RRD_ALGORITHM_ABSOLUTE); + + if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT) + m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else rrdset_next(m->st); + + rrddim_set_by_pointer(m->st, m->rd_value, m->last); + + if(m->rd_count) + rrddim_set_by_pointer(m->st, m->rd_count, m->events); + + rrdset_done(m->st); +} + +static inline void statsd_private_chart_counter_or_meter(STATSD_METRIC *m, const char *dim, const char *family) { + debug(D_STATSD, "updating private chart for %s metric '%s'", dim, m->name); + + if(unlikely(!m->st)) { + char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1]; + statsd_get_metric_type_and_id(m, type, id, dim, RRD_ID_LENGTH_MAX); + + m->st = statsd_private_rrdset_create( + m + , type + , id + , NULL // name + , family // family (submenu) + , m->name // context + , m->name // title + , "events/s" // units + , STATSD_CHART_PRIORITY + , statsd.update_every + , RRDSET_TYPE_AREA + ); + + m->rd_value = rrddim_add(m->st, dim, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT) + m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else rrdset_next(m->st); + + rrddim_set_by_pointer(m->st, m->rd_value, m->last); + + if(m->rd_count) + rrddim_set_by_pointer(m->st, m->rd_count, m->events); + + rrdset_done(m->st); +} + +static inline void statsd_private_chart_set(STATSD_METRIC *m) { + debug(D_STATSD, "updating private chart for set metric '%s'", m->name); + + if(unlikely(!m->st)) { + char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1]; + statsd_get_metric_type_and_id(m, type, id, "set", RRD_ID_LENGTH_MAX); + + m->st = statsd_private_rrdset_create( + m + , type + , id + , NULL // name + , "sets" // family (submenu) + , m->name // context + , m->name // title + , "entries" // units + , STATSD_CHART_PRIORITY + , statsd.update_every + , RRDSET_TYPE_LINE + ); + + m->rd_value = rrddim_add(m->st, "set", "set size", 1, 1, RRD_ALGORITHM_ABSOLUTE); + + if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT) + m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else rrdset_next(m->st); + + rrddim_set_by_pointer(m->st, m->rd_value, m->last); + + if(m->rd_count) + rrddim_set_by_pointer(m->st, m->rd_count, m->events); + + rrdset_done(m->st); +} + +static inline void statsd_private_chart_timer_or_histogram(STATSD_METRIC *m, const char *dim, const char *family, const char *units) { + debug(D_STATSD, "updating private chart for %s metric '%s'", dim, m->name); + + if(unlikely(!m->st)) { + char type[RRD_ID_LENGTH_MAX + 1], id[RRD_ID_LENGTH_MAX + 1]; + statsd_get_metric_type_and_id(m, type, id, dim, RRD_ID_LENGTH_MAX); + + m->st = statsd_private_rrdset_create( + m + , type + , id + , NULL // name + , family // family (submenu) + , m->name // context + , m->name // title + , units // units + , STATSD_CHART_PRIORITY + , statsd.update_every + , RRDSET_TYPE_AREA + ); + + m->histogram.ext->rd_min = rrddim_add(m->st, "min", NULL, 1, STATSD_DECIMAL_DETAIL, RRD_ALGORITHM_ABSOLUTE); + m->histogram.ext->rd_max = rrddim_add(m->st, "max", NULL, 1, STATSD_DECIMAL_DETAIL, RRD_ALGORITHM_ABSOLUTE); + m->rd_value = rrddim_add(m->st, "average", NULL, 1, STATSD_DECIMAL_DETAIL, RRD_ALGORITHM_ABSOLUTE); + m->histogram.ext->rd_percentile = rrddim_add(m->st, statsd.histogram_percentile_str, NULL, 1, STATSD_DECIMAL_DETAIL, RRD_ALGORITHM_ABSOLUTE); + m->histogram.ext->rd_median = rrddim_add(m->st, "median", NULL, 1, STATSD_DECIMAL_DETAIL, RRD_ALGORITHM_ABSOLUTE); + m->histogram.ext->rd_stddev = rrddim_add(m->st, "stddev", NULL, 1, STATSD_DECIMAL_DETAIL, RRD_ALGORITHM_ABSOLUTE); + m->histogram.ext->rd_sum = rrddim_add(m->st, "sum", NULL, 1, STATSD_DECIMAL_DETAIL, RRD_ALGORITHM_ABSOLUTE); + + if(m->options & STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT) + m->rd_count = rrddim_add(m->st, "events", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else rrdset_next(m->st); + + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_min, m->histogram.ext->last_min); + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_max, m->histogram.ext->last_max); + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_percentile, m->histogram.ext->last_percentile); + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_median, m->histogram.ext->last_median); + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_stddev, m->histogram.ext->last_stddev); + rrddim_set_by_pointer(m->st, m->histogram.ext->rd_sum, m->histogram.ext->last_sum); + rrddim_set_by_pointer(m->st, m->rd_value, m->last); + + if(m->rd_count) + rrddim_set_by_pointer(m->st, m->rd_count, m->events); + + rrdset_done(m->st); +} + +// -------------------------------------------------------------------------------------------------------------------- +// statsd flush metrics + +static inline void statsd_flush_gauge(STATSD_METRIC *m) { + debug(D_STATSD, "flushing gauge metric '%s'", m->name); + + int updated = 0; + if(m->count && !m->reset) { + m->last = (collected_number) (m->gauge.value * STATSD_DECIMAL_DETAIL); + + m->reset = 1; + updated = 1; + } + + if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))) + statsd_private_chart_gauge(m); +} + +static inline void statsd_flush_counter_or_meter(STATSD_METRIC *m, const char *dim, const char *family) { + debug(D_STATSD, "flushing %s metric '%s'", dim, m->name); + + int updated = 0; + if(m->count && !m->reset) { + m->last = m->counter.value; + + m->reset = 1; + updated = 1; + } + + if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))) + statsd_private_chart_counter_or_meter(m, dim, family); +} + +static inline void statsd_flush_counter(STATSD_METRIC *m) { + statsd_flush_counter_or_meter(m, "counter", "counters"); +} + +static inline void statsd_flush_meter(STATSD_METRIC *m) { + statsd_flush_counter_or_meter(m, "meter", "meters"); +} + +static inline void statsd_flush_set(STATSD_METRIC *m) { + debug(D_STATSD, "flushing set metric '%s'", m->name); + + int updated = 0; + if(m->count && !m->reset) { + m->last = (collected_number)m->set.unique; + + m->reset = 1; + updated = 1; + } + + if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))) + statsd_private_chart_set(m); +} + +static inline void statsd_flush_timer_or_histogram(STATSD_METRIC *m, const char *dim, const char *family, const char *units) { + debug(D_STATSD, "flushing %s metric '%s'", dim, m->name); + + netdata_mutex_lock(&m->histogram.ext->mutex); + + int updated = 0; + if(m->count && !m->reset && m->histogram.ext->used > 0) { + size_t len = m->histogram.ext->used; + long double *series = m->histogram.ext->values; + sort_series(series, len); + + m->histogram.ext->last_min = (collected_number)roundl(series[0] * STATSD_DECIMAL_DETAIL); + m->histogram.ext->last_max = (collected_number)roundl(series[len - 1] * STATSD_DECIMAL_DETAIL); + m->last = (collected_number)roundl(average(series, len) * STATSD_DECIMAL_DETAIL); + m->histogram.ext->last_median = (collected_number)roundl(median_on_sorted_series(series, len) * STATSD_DECIMAL_DETAIL); + m->histogram.ext->last_stddev = (collected_number)roundl(standard_deviation(series, len) * STATSD_DECIMAL_DETAIL); + m->histogram.ext->last_sum = (collected_number)roundl(sum(series, len) * STATSD_DECIMAL_DETAIL); + + size_t pct_len = (size_t)floor((double)len * statsd.histogram_percentile / 100.0); + if(pct_len < 1) + m->histogram.ext->last_percentile = (collected_number)(series[0] * STATSD_DECIMAL_DETAIL); + else + m->histogram.ext->last_percentile = (collected_number)roundl(average(series, pct_len) * STATSD_DECIMAL_DETAIL); + + debug(D_STATSD, "STATSD %s metric %s: min " COLLECTED_NUMBER_FORMAT ", max " COLLECTED_NUMBER_FORMAT ", last " COLLECTED_NUMBER_FORMAT ", pcent " COLLECTED_NUMBER_FORMAT ", median " COLLECTED_NUMBER_FORMAT ", stddev " COLLECTED_NUMBER_FORMAT ", sum " COLLECTED_NUMBER_FORMAT, + dim, m->name, m->histogram.ext->last_min, m->histogram.ext->last_max, m->last, m->histogram.ext->last_percentile, m->histogram.ext->last_median, m->histogram.ext->last_stddev, m->histogram.ext->last_sum); + + m->reset = 1; + updated = 1; + } + + + if(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED && (updated || !(m->options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED))) + statsd_private_chart_timer_or_histogram(m, dim, family, units); + + netdata_mutex_unlock(&m->histogram.ext->mutex); +} + +static inline void statsd_flush_timer(STATSD_METRIC *m) { + statsd_flush_timer_or_histogram(m, "timer", "timers", "milliseconds"); +} + +static inline void statsd_flush_histogram(STATSD_METRIC *m) { + statsd_flush_timer_or_histogram(m, "histogram", "histograms", "value"); +} + +static inline RRD_ALGORITHM statsd_algorithm_for_metric(STATSD_METRIC *m) { + switch(m->type) { + default: + case STATSD_METRIC_TYPE_GAUGE: + case STATSD_METRIC_TYPE_SET: + case STATSD_METRIC_TYPE_TIMER: + case STATSD_METRIC_TYPE_HISTOGRAM: + return RRD_ALGORITHM_ABSOLUTE; + + case STATSD_METRIC_TYPE_METER: + case STATSD_METRIC_TYPE_COUNTER: + return RRD_ALGORITHM_INCREMENTAL; + } +} + +static inline void check_if_metric_is_for_app(STATSD_INDEX *index, STATSD_METRIC *m) { + (void)index; + + STATSD_APP *app; + for(app = statsd.apps; app ;app = app->next) { + if(unlikely(simple_pattern_matches(app->metrics, m->name))) { + debug(D_STATSD, "metric '%s' matches app '%s'", m->name, app->name); + + // the metric should get the options from the app + + if(app->default_options & STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED) + m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED; + else + m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED; + + if(app->default_options & STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED) + m->options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; + else + m->options &= ~STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; + + m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED; + + // check if there is a chart in this app, willing to get this metric + STATSD_APP_CHART *chart; + for(chart = app->charts; chart; chart = chart->next) { + STATSD_APP_CHART_DIM *dim; + for(dim = chart->dimensions; dim ; dim = dim->next) { + if(!dim->value_ptr && dim->metric_hash == m->hash && !strcmp(dim->metric, m->name)) { + // we have a match - this metric should be linked to this dimension + + if(dim->value_type == STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS) { + dim->value_ptr = &m->events; + dim->algorithm = RRD_ALGORITHM_INCREMENTAL; + } + else if(m->type == STATSD_METRIC_TYPE_HISTOGRAM || m->type == STATSD_METRIC_TYPE_TIMER) { + dim->algorithm = RRD_ALGORITHM_ABSOLUTE; + dim->divisor *= STATSD_DECIMAL_DETAIL; + + switch(dim->value_type) { + case STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS: + // will never match - added to avoid warning + break; + + case STATSD_APP_CHART_DIM_VALUE_TYPE_LAST: + case STATSD_APP_CHART_DIM_VALUE_TYPE_AVERAGE: + dim->value_ptr = &m->last; + break; + + case STATSD_APP_CHART_DIM_VALUE_TYPE_SUM: + dim->value_ptr = &m->histogram.ext->last_sum; + break; + + case STATSD_APP_CHART_DIM_VALUE_TYPE_MIN: + dim->value_ptr = &m->histogram.ext->last_min; + break; + + case STATSD_APP_CHART_DIM_VALUE_TYPE_MAX: + dim->value_ptr = &m->histogram.ext->last_max; + break; + + case STATSD_APP_CHART_DIM_VALUE_TYPE_MEDIAN: + dim->value_ptr = &m->histogram.ext->last_median; + break; + + case STATSD_APP_CHART_DIM_VALUE_TYPE_PERCENTILE: + dim->value_ptr = &m->histogram.ext->last_percentile; + break; + + case STATSD_APP_CHART_DIM_VALUE_TYPE_STDDEV: + dim->value_ptr = &m->histogram.ext->last_stddev; + break; + } + } + else { + if (dim->value_type != STATSD_APP_CHART_DIM_VALUE_TYPE_LAST) + error("STATSD: unsupported value type for dimension '%s' of chart '%s' of app '%s' on metric '%s'", dim->name, chart->id, app->name, m->name); + + dim->value_ptr = &m->last; + dim->algorithm = statsd_algorithm_for_metric(m); + + if(m->type == STATSD_METRIC_TYPE_GAUGE) + dim->divisor *= STATSD_DECIMAL_DETAIL; + } + + if(unlikely(chart->st && dim->rd)) { + rrddim_set_algorithm(chart->st, dim->rd, dim->algorithm); + rrddim_set_multiplier(chart->st, dim->rd, dim->multiplier); + rrddim_set_divisor(chart->st, dim->rd, dim->divisor); + } + + chart->dimensions_linked_count++; + debug(D_STATSD, "metric '%s' of type %u linked with app '%s', chart '%s', dimension '%s', algorithm '%s'", m->name, m->type, app->name, chart->id, dim->name, rrd_algorithm_name(dim->algorithm)); + } + } + } + } + } +} + +static inline void statsd_update_app_chart(STATSD_APP *app, STATSD_APP_CHART *chart) { + debug(D_STATSD, "updating chart '%s' for app '%s'", chart->id, app->name); + + if(!chart->st) { + chart->st = rrdset_create_custom( + localhost + , app->name + , chart->id + , chart->name + , chart->family + , chart->context + , chart->title + , chart->units + , chart->priority + , statsd.update_every + , chart->chart_type + , app->rrd_memory_mode + , app->rrd_history_entries + ); + + rrdset_flag_set(chart->st, RRDSET_FLAG_STORE_FIRST); + // rrdset_flag_set(chart->st, RRDSET_FLAG_DEBUG); + } + else rrdset_next(chart->st); + + STATSD_APP_CHART_DIM *dim; + for(dim = chart->dimensions; dim ;dim = dim->next) { + if(unlikely(!dim->rd)) + dim->rd = rrddim_add(chart->st, dim->name, NULL, dim->multiplier, dim->divisor, dim->algorithm); + + if(unlikely(dim->value_ptr)) { + debug(D_STATSD, "updating dimension '%s' (%s) of chart '%s' (%s) for app '%s' with value " COLLECTED_NUMBER_FORMAT, dim->name, dim->rd->id, chart->id, chart->st->id, app->name, *dim->value_ptr); + rrddim_set_by_pointer(chart->st, dim->rd, *dim->value_ptr); + } + } + + rrdset_done(chart->st); + debug(D_STATSD, "completed update of chart '%s' for app '%s'", chart->id, app->name); +} + +static inline void statsd_update_all_app_charts(void) { + // debug(D_STATSD, "updating app charts"); + + STATSD_APP *app; + for(app = statsd.apps; app ;app = app->next) { + // debug(D_STATSD, "updating charts for app '%s'", app->name); + + STATSD_APP_CHART *chart; + for(chart = app->charts; chart ;chart = chart->next) { + if(unlikely(chart->dimensions_linked_count)) { + statsd_update_app_chart(app, chart); + } + } + } + + // debug(D_STATSD, "completed update of app charts"); +} + +static inline void statsd_flush_index_metrics(STATSD_INDEX *index, void (*flush_metric)(STATSD_METRIC *)) { + STATSD_METRIC *m; + for(m = index->first; m ; m = m->next) { + if(unlikely(!(m->options & STATSD_METRIC_OPTION_CHECKED_IN_APPS))) { + check_if_metric_is_for_app(index, m); + m->options |= STATSD_METRIC_OPTION_CHECKED_IN_APPS; + } + + if(unlikely(!(m->options & STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED))) { + if(statsd.private_charts >= statsd.max_private_charts_hard) { + debug(D_STATSD, "STATSD: metric '%s' will not be charted, because the hard limit of the maximum number of charts has been reached.", m->name); + info("STATSD: metric '%s' will not be charted, because the hard limit of the maximum number of charts (%zu) has been reached. Increase the number of charts by editing netdata.conf, [statsd] section.", m->name, statsd.max_private_charts); + m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED; + } + else { + if (simple_pattern_matches(statsd.charts_for, m->name)) { + debug(D_STATSD, "STATSD: metric '%s' will be charted.", m->name); + m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED; + } else { + debug(D_STATSD, "STATSD: metric '%s' will not be charted.", m->name); + m->options &= ~STATSD_METRIC_OPTION_PRIVATE_CHART_ENABLED; + } + } + + m->options |= STATSD_METRIC_OPTION_PRIVATE_CHART_CHECKED; + } + + flush_metric(m); + } +} + + +// -------------------------------------------------------------------------------------- +// statsd main thread + +int statsd_listen_sockets_setup(void) { + return listen_sockets_setup(&statsd.sockets); +} + +void statsd_main_cleanup(void *data) { + pthread_t *threads = data; + + int i; + for(i = 0; i < statsd.threads ;i++) + pthread_cancel(threads[i]); + + listen_sockets_close(&statsd.sockets); +} + +void *statsd_main(void *ptr) { + (void)ptr; + + info("STATSD main thread created with task id %d", gettid()); + + if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) + error("Cannot set pthread cancel type to DEFERRED."); + + if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + error("Cannot set pthread cancel state to ENABLE."); + + // ---------------------------------------------------------------------------------------------------------------- + // statsd configuration + + statsd.enabled = config_get_boolean(CONFIG_SECTION_STATSD, "enabled", statsd.enabled); + + statsd.update_every = default_rrd_update_every; + statsd.update_every = (int)config_get_number(CONFIG_SECTION_STATSD, "update every (flushInterval)", statsd.update_every); + if(statsd.update_every < default_rrd_update_every) { + error("STATSD: minimum flush interval %d given, but the minimum is the update every of netdata. Using %d", statsd.update_every, default_rrd_update_every); + statsd.update_every = default_rrd_update_every; + } + +#ifdef HAVE_RECVMMSG + statsd.recvmmsg_size = (size_t)config_get_number(CONFIG_SECTION_STATSD, "udp messages to process at once", (long long)statsd.recvmmsg_size); +#endif + + statsd.charts_for = simple_pattern_create(config_get(CONFIG_SECTION_STATSD, "create private charts for metrics matching", "*"), SIMPLE_PATTERN_EXACT); + statsd.max_private_charts = (size_t)config_get_number(CONFIG_SECTION_STATSD, "max private charts allowed", (long long)statsd.max_private_charts); + statsd.max_private_charts_hard = (size_t)config_get_number(CONFIG_SECTION_STATSD, "max private charts hard limit", (long long)statsd.max_private_charts * 5); + statsd.private_charts_memory_mode = rrd_memory_mode_id(config_get(CONFIG_SECTION_STATSD, "private charts memory mode", rrd_memory_mode_name(default_rrd_memory_mode))); + statsd.private_charts_rrd_history_entries = (int)config_get_number(CONFIG_SECTION_STATSD, "private charts history", default_rrd_history_entries); + + statsd.histogram_percentile = (double)config_get_float(CONFIG_SECTION_STATSD, "histograms and timers percentile (percentThreshold)", statsd.histogram_percentile); + if(isless(statsd.histogram_percentile, 0) || isgreater(statsd.histogram_percentile, 100)) { + error("STATSD: invalid histograms and timers percentile %0.5f given", statsd.histogram_percentile); + statsd.histogram_percentile = 95.0; + } + { + char buffer[100 + 1]; + snprintf(buffer, 100, "%0.1f%%", statsd.histogram_percentile); + statsd.histogram_percentile_str = strdupz(buffer); + } + + if(config_get_boolean(CONFIG_SECTION_STATSD, "add dimension for number of events received", 1)) { + statsd.gauges.default_options |= STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT; + statsd.counters.default_options |= STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT; + statsd.meters.default_options |= STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT; + statsd.sets.default_options |= STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT; + statsd.histograms.default_options |= STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT; + statsd.timers.default_options |= STATSD_METRIC_OPTION_CHART_DIMENSION_COUNT; + } + + if(config_get_boolean(CONFIG_SECTION_STATSD, "gaps on gauges (deleteGauges)", 0)) + statsd.gauges.default_options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; + + if(config_get_boolean(CONFIG_SECTION_STATSD, "gaps on counters (deleteCounters)", 0)) + statsd.counters.default_options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; + + if(config_get_boolean(CONFIG_SECTION_STATSD, "gaps on meters (deleteMeters)", 0)) + statsd.meters.default_options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; + + if(config_get_boolean(CONFIG_SECTION_STATSD, "gaps on sets (deleteSets)", 0)) + statsd.sets.default_options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; + + if(config_get_boolean(CONFIG_SECTION_STATSD, "gaps on histograms (deleteHistograms)", 0)) + statsd.histograms.default_options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; + + if(config_get_boolean(CONFIG_SECTION_STATSD, "gaps on timers (deleteTimers)", 0)) + statsd.timers.default_options |= STATSD_METRIC_OPTION_SHOW_GAPS_WHEN_NOT_COLLECTED; + +#ifdef STATSD_MULTITHREADED + statsd.threads = (int)config_get_number(CONFIG_SECTION_STATSD, "threads", processors); + if(statsd.threads < 1) { + error("STATSD: Invalid number of threads %d, using %d", statsd.threads, processors); + statsd.threads = processors; + config_set_number(CONFIG_SECTION_STATSD, "collector threads", statsd.threads); + } +#else + statsd.threads = 1; +#endif + + // read custom application definitions + { + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s/statsd.d", netdata_configured_config_dir); + statsd_readdir(filename); + } + + // ---------------------------------------------------------------------------------------------------------------- + // statsd setup + + if(!statsd.enabled) return NULL; + + statsd_listen_sockets_setup(); + if(!statsd.sockets.opened) { + error("STATSD: No statsd sockets to listen to. statsd will be disabled."); + pthread_exit(NULL); + } + + pthread_t threads[statsd.threads]; + int i; + + for(i = 0; i < statsd.threads ;i++) { + if(pthread_create(&threads[i], NULL, statsd_collector_thread, &i)) + error("STATSD: failed to create child thread."); + + else if(pthread_detach(threads[i])) + error("STATSD: cannot request detach of child thread."); + } + + pthread_cleanup_push(statsd_main_cleanup, &threads); + + // ---------------------------------------------------------------------------------------------------------------- + // statsd monitoring charts + + RRDSET *st_metrics = rrdset_create_localhost( + "netdata" + , "statsd_metrics" + , NULL + , "statsd" + , NULL + , "Metrics in the netdata statsd database" + , "metrics" + , 132000 + , statsd.update_every + , RRDSET_TYPE_STACKED + ); + RRDDIM *rd_metrics_gauge = rrddim_add(st_metrics, "gauges", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + RRDDIM *rd_metrics_counter = rrddim_add(st_metrics, "counters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + RRDDIM *rd_metrics_timer = rrddim_add(st_metrics, "timers", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + RRDDIM *rd_metrics_meter = rrddim_add(st_metrics, "meters", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + RRDDIM *rd_metrics_histogram = rrddim_add(st_metrics, "histograms", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + RRDDIM *rd_metrics_set = rrddim_add(st_metrics, "sets", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + RRDSET *st_events = rrdset_create_localhost( + "netdata" + , "statsd_events" + , NULL + , "statsd" + , NULL + , "Events processed by the netdata statsd server" + , "events/s" + , 132001 + , statsd.update_every + , RRDSET_TYPE_STACKED + ); + RRDDIM *rd_events_gauge = rrddim_add(st_events, "gauges", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_events_counter = rrddim_add(st_events, "counters", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_events_timer = rrddim_add(st_events, "timers", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_events_meter = rrddim_add(st_events, "meters", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_events_histogram = rrddim_add(st_events, "histograms", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_events_set = rrddim_add(st_events, "sets", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_events_unknown = rrddim_add(st_events, "unknown", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_events_errors = rrddim_add(st_events, "errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + RRDSET *st_reads = rrdset_create_localhost( + "netdata" + , "statsd_reads" + , NULL + , "statsd" + , NULL + , "Read operations made by the netdata statsd server" + , "reads/s" + , 132002 + , statsd.update_every + , RRDSET_TYPE_STACKED + ); + RRDDIM *rd_reads_tcp = rrddim_add(st_reads, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_reads_udp = rrddim_add(st_reads, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + RRDSET *st_bytes = rrdset_create_localhost( + "netdata" + , "statsd_bytes" + , NULL + , "statsd" + , NULL + , "Bytes read by the netdata statsd server" + , "kbps" + , 132003 + , statsd.update_every + , RRDSET_TYPE_STACKED + ); + RRDDIM *rd_bytes_tcp = rrddim_add(st_bytes, "tcp", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_bytes_udp = rrddim_add(st_bytes, "udp", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + + RRDSET *st_packets = rrdset_create_localhost( + "netdata" + , "statsd_packets" + , NULL + , "statsd" + , NULL + , "Network packets processed by the netdata statsd server" + , "packets/s" + , 132004 + , statsd.update_every + , RRDSET_TYPE_STACKED + ); + RRDDIM *rd_packets_tcp = rrddim_add(st_packets, "tcp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + RRDDIM *rd_packets_udp = rrddim_add(st_packets, "udp", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + RRDSET *st_pcharts = rrdset_create_localhost( + "netdata" + , "private_charts" + , NULL + , "statsd" + , NULL + , "Private metric charts created by the netdata statsd server" + , "charts" + , 132010 + , statsd.update_every + , RRDSET_TYPE_AREA + ); + RRDDIM *rd_pcharts = rrddim_add(st_pcharts, "charts", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + + // ---------------------------------------------------------------------------------------------------------------- + // statsd thread to turn metrics into charts + + usec_t step = statsd.update_every * USEC_PER_SEC; + heartbeat_t hb; + heartbeat_init(&hb); + for(;;) { + usec_t hb_dt = heartbeat_next(&hb, step); + + if(unlikely(netdata_exit)) + break; + + statsd_flush_index_metrics(&statsd.gauges, statsd_flush_gauge); + statsd_flush_index_metrics(&statsd.counters, statsd_flush_counter); + statsd_flush_index_metrics(&statsd.meters, statsd_flush_meter); + statsd_flush_index_metrics(&statsd.timers, statsd_flush_timer); + statsd_flush_index_metrics(&statsd.histograms, statsd_flush_histogram); + statsd_flush_index_metrics(&statsd.sets, statsd_flush_set); + + statsd_update_all_app_charts(); + + if(unlikely(netdata_exit)) + break; + + if(hb_dt) { + rrdset_next(st_metrics); + rrdset_next(st_events); + rrdset_next(st_reads); + rrdset_next(st_bytes); + rrdset_next(st_packets); + rrdset_next(st_pcharts); + } + + rrddim_set_by_pointer(st_metrics, rd_metrics_gauge, (collected_number)statsd.gauges.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_counter, (collected_number)statsd.counters.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_timer, (collected_number)statsd.timers.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_meter, (collected_number)statsd.meters.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_histogram, (collected_number)statsd.histograms.metrics); + rrddim_set_by_pointer(st_metrics, rd_metrics_set, (collected_number)statsd.sets.metrics); + + rrddim_set_by_pointer(st_events, rd_events_gauge, (collected_number)statsd.gauges.events); + rrddim_set_by_pointer(st_events, rd_events_counter, (collected_number)statsd.counters.events); + rrddim_set_by_pointer(st_events, rd_events_timer, (collected_number)statsd.timers.events); + rrddim_set_by_pointer(st_events, rd_events_meter, (collected_number)statsd.meters.events); + rrddim_set_by_pointer(st_events, rd_events_histogram, (collected_number)statsd.histograms.events); + rrddim_set_by_pointer(st_events, rd_events_set, (collected_number)statsd.sets.events); + rrddim_set_by_pointer(st_events, rd_events_unknown, (collected_number)statsd.unknown_types); + rrddim_set_by_pointer(st_events, rd_events_errors, (collected_number)statsd.socket_errors); + + rrddim_set_by_pointer(st_reads, rd_reads_tcp, (collected_number)statsd.tcp_socket_reads); + rrddim_set_by_pointer(st_reads, rd_reads_udp, (collected_number)statsd.udp_socket_reads); + + rrddim_set_by_pointer(st_bytes, rd_bytes_tcp, (collected_number)statsd.tcp_bytes_read); + rrddim_set_by_pointer(st_bytes, rd_bytes_udp, (collected_number)statsd.udp_bytes_read); + + rrddim_set_by_pointer(st_packets, rd_packets_tcp, (collected_number)statsd.tcp_packets_received); + rrddim_set_by_pointer(st_packets, rd_packets_udp, (collected_number)statsd.udp_packets_received); + + rrddim_set_by_pointer(st_pcharts, rd_pcharts, (collected_number)statsd.private_charts); + + if(unlikely(netdata_exit)) + break; + + rrdset_done(st_metrics); + rrdset_done(st_events); + rrdset_done(st_reads); + rrdset_done(st_bytes); + rrdset_done(st_packets); + rrdset_done(st_pcharts); + + if(unlikely(netdata_exit)) + break; + } + + pthread_cleanup_pop(1); + + pthread_exit(NULL); + return NULL; +} diff --git a/src/statsd.h b/src/statsd.h new file mode 100644 index 000000000..17af098e8 --- /dev/null +++ b/src/statsd.h @@ -0,0 +1,9 @@ +#ifndef NETDATA_STATSD_H +#define NETDATA_STATSD_H + +#define STATSD_LISTEN_PORT 8125 +#define STATSD_LISTEN_BACKLOG 4096 + +extern void *statsd_main(void *ptr); + +#endif //NETDATA_STATSD_H diff --git a/src/storage_number.h b/src/storage_number.h index 74d24a322..34ed0d89c 100644 --- a/src/storage_number.h +++ b/src/storage_number.h @@ -29,6 +29,7 @@ typedef uint32_t storage_number; // extract the flags #define get_storage_number_flags(value) ((((storage_number)value) & (1 << 24)) | (((storage_number)value) & (2 << 24)) | (((storage_number)value) & (4 << 24))) +#define SN_EMPTY_SLOT 0x00000000 // checks #define does_storage_number_exist(value) ((get_storage_number_flags(value) != 0)?1:0) diff --git a/src/sys_fs_cgroup.c b/src/sys_fs_cgroup.c index 8f31527de..0f9c8854a 100644 --- a/src/sys_fs_cgroup.c +++ b/src/sys_fs_cgroup.c @@ -147,11 +147,17 @@ 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 + // ---------------------------------------------------------------- + + " !*/init.scope " // ignore init.scope + " *.scope " // we need all *.scope for sure + + // ---------------------------------------------------------------- + + " !*/vcpu* " // libvirtd adds these sub-cgroups + " !*/emulator " // libvirtd adds these sub-cgroups " !*.mount " " !*.partition " - " !*.scope " " !*.service " " !*.slice " " !*.swap " @@ -171,12 +177,14 @@ void read_cgroup_plugin_configuration() { enabled_cgroup_paths = simple_pattern_create( config_get("plugin:cgroups", "search for cgroups in subpaths matching", - " !*-qemu " // #345 + " !*/init.scope " // ignore init.scope + " !*-qemu " // #345 " !/init.scope " " !/system " " !/systemd " " !/user " " !/user.slice " + " !/lxc/*/ns/* " // #2161 " * " ), SIMPLE_PATTERN_EXACT); @@ -185,13 +193,13 @@ void read_cgroup_plugin_configuration() { enabled_cgroup_renames = simple_pattern_create( config_get("plugin:cgroups", "run script to rename cgroups matching", - " /qemu.slice/*.scope " // #1949 + " *.scope " " *docker* " " *lxc* " + " *qemu* " " !/ " " !*.mount " " !*.partition " - " !*.scope " " !*.service " " !*.slice " " !*.swap " @@ -893,20 +901,20 @@ 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); + if(cg->st_cpu) rrdset_is_obsolete(cg->st_cpu); + if(cg->st_cpu_per_core) rrdset_is_obsolete(cg->st_cpu_per_core); + if(cg->st_mem) rrdset_is_obsolete(cg->st_mem); + if(cg->st_writeback) rrdset_is_obsolete(cg->st_writeback); + if(cg->st_mem_activity) rrdset_is_obsolete(cg->st_mem_activity); + if(cg->st_pgfaults) rrdset_is_obsolete(cg->st_pgfaults); + if(cg->st_mem_usage) rrdset_is_obsolete(cg->st_mem_usage); + if(cg->st_mem_failcnt) rrdset_is_obsolete(cg->st_mem_failcnt); + if(cg->st_io) rrdset_is_obsolete(cg->st_io); + if(cg->st_serviced_ops) rrdset_is_obsolete(cg->st_serviced_ops); + if(cg->st_throttle_io) rrdset_is_obsolete(cg->st_throttle_io); + if(cg->st_throttle_serviced_ops) rrdset_is_obsolete(cg->st_throttle_serviced_ops); + if(cg->st_queued_ops) rrdset_is_obsolete(cg->st_queued_ops); + if(cg->st_merged_ops) rrdset_is_obsolete(cg->st_merged_ops); freez(cg->cpuacct_usage.cpu_percpu); diff --git a/src/unit_test.c b/src/unit_test.c index 0866d215c..9b008138f 100644 --- a/src/unit_test.c +++ b/src/unit_test.c @@ -17,8 +17,8 @@ int check_storage_number(calculated_number n, int debug) { if(dcdiff < 0) dcdiff = -dcdiff; - size_t len = print_calculated_number(buffer, d); - calculated_number p = str2l(buffer); + size_t len = (size_t)print_calculated_number(buffer, d); + calculated_number p = str2ld(buffer, NULL); calculated_number pdiff = n - p; calculated_number pcdiff = pdiff * 100.0 / n; if(pcdiff < 0) pcdiff = -pcdiff; @@ -229,6 +229,45 @@ int unit_test_storage() return r; } +int unit_test_str2ld() { + char *values[] = { + "1.234567", "-35.6", "0.00123", "23842384234234.2", ".1", "1.2e-10", + "hello", "1wrong", "nan", "inf", NULL + }; + + int i; + for(i = 0; values[i] ; i++) { + char *e_mine = "hello", *e_sys = "world"; + long double mine = str2ld(values[i], &e_mine); + long double sys = strtold(values[i], &e_sys); + + if(isnan(mine)) { + if(!isnan(sys)) { + fprintf(stderr, "Value '%s' is parsed as %Lf, but system believes it is %Lf.\n", values[i], mine, sys); + return -1; + } + } + else if(isinf(mine)) { + if(!isinf(sys)) { + fprintf(stderr, "Value '%s' is parsed as %Lf, but system believes it is %Lf.\n", values[i], mine, sys); + return -1; + } + } + else if(mine != sys && abs(mine-sys) > 0.000001) { + fprintf(stderr, "Value '%s' is parsed as %Lf, but system believes it is %Lf, delta %Lf.\n", values[i], mine, sys, sys-mine); + return -1; + } + + if(e_mine != e_sys) { + fprintf(stderr, "Value '%s' is parsed correctly, but endptr is not right\n", values[i]); + return -1; + } + + fprintf(stderr, "str2ld() parsed value '%s' exactly the same way with strtold(), returned %Lf vs %Lf\n", values[i], mine, sys); + } + + return 0; +} // -------------------------------------------------------------------------------------------------------------------- @@ -244,7 +283,7 @@ struct test { int update_every; unsigned long long multiplier; unsigned long long divisor; - int algorithm; + RRD_ALGORITHM algorithm; unsigned long feed_entries; unsigned long result_entries; @@ -884,7 +923,7 @@ int run_test(struct test *test) { fprintf(stderr, "\nRunning test '%s':\n%s\n", test->name, test->description); - default_rrd_memory_mode = RRD_MEMORY_MODE_RAM; + default_rrd_memory_mode = RRD_MEMORY_MODE_ALLOC; default_rrd_update_every = test->update_every; char name[101]; @@ -916,7 +955,9 @@ int run_test(struct test *test) (float)time_now / 1000000.0, ((calculated_number)test->feed[c].value - (calculated_number)last) * (calculated_number)test->multiplier / (calculated_number)test->divisor, (((calculated_number)test->feed[c].value - (calculated_number)last) * (calculated_number)test->multiplier / (calculated_number)test->divisor) / (calculated_number)test->feed[c].microseconds * (calculated_number)1000000); - rrdset_next_usec_unfiltered(st, test->feed[c].microseconds); + + // rrdset_next_usec_unfiltered(st, test->feed[c].microseconds); + st->usec_since_last_update = test->feed[c].microseconds; } else { fprintf(stderr, " > %s: feeding position %lu\n", test->name, c+1); @@ -1091,7 +1132,7 @@ int unit_test(long delay, long shift) snprintfz(name, 100, "unittest-%d-%ld-%ld", repeat, delay, shift); //debug_flags = 0xffffffff; - default_rrd_memory_mode = RRD_MEMORY_MODE_RAM; + default_rrd_memory_mode = RRD_MEMORY_MODE_ALLOC; default_rrd_update_every = 1; int do_abs = 1; @@ -1125,7 +1166,8 @@ int unit_test(long delay, long shift) fprintf(stderr, "\n\nLOOP = %lu, DELAY = %ld, VALUE = " COLLECTED_NUMBER_FORMAT "\n", c, delay, i); if(c) { - rrdset_next_usec_unfiltered(st, delay); + // rrdset_next_usec_unfiltered(st, delay); + st->usec_since_last_update = delay; } if(do_abs) rrddim_set(st, "absolute", i); if(do_inc) rrddim_set(st, "incremental", i); diff --git a/src/unit_test.h b/src/unit_test.h index 916ad71f2..3240b5f0e 100644 --- a/src/unit_test.h +++ b/src/unit_test.h @@ -4,5 +4,6 @@ extern int unit_test_storage(void); extern int unit_test(long delay, long shift); extern int run_all_mockup_tests(void); +extern int unit_test_str2ld(void); #endif /* NETDATA_UNIT_TEST_H */ diff --git a/src/web_api_v1.c b/src/web_api_v1.c index 0acc43acb..3ffd8c324 100644 --- a/src/web_api_v1.c +++ b/src/web_api_v1.c @@ -208,6 +208,10 @@ inline int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) { int format = ALLMETRICS_SHELL; + int help = 0, types = 0, names = backend_send_names; // prometheus options + const char *prometheus_server = w->client_ip; + uint32_t prometheus_options = backend_options; + const char *prometheus_prefix = backend_prefix; while(url) { char *value = mystrsep(&url, "?&"); @@ -222,11 +226,40 @@ inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client format = ALLMETRICS_SHELL; else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS)) format = ALLMETRICS_PROMETHEUS; + else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS)) + format = ALLMETRICS_PROMETHEUS_ALL_HOSTS; else if(!strcmp(value, ALLMETRICS_FORMAT_JSON)) format = ALLMETRICS_JSON; else format = 0; } + else if(!strcmp(name, "help")) { + if(!strcmp(value, "yes")) + help = 1; + else + help = 0; + } + else if(!strcmp(name, "types")) { + if(!strcmp(value, "yes")) + types = 1; + else + types = 0; + } + else if(!strcmp(name, "names")) { + if(!strcmp(value, "yes")) + names = 1; + else + names = 0; + } + else if(!strcmp(name, "server")) { + prometheus_server = value; + } + else if(!strcmp(name, "prefix")) { + prometheus_prefix = value; + } + else if(!strcmp(name, "data") || !strcmp(name, "source") || !strcmp(name, "data source") || !strcmp(name, "data-source") || !strcmp(name, "data_source") || !strcmp(name, "datasource")) { + prometheus_options = backend_parse_data_source(value, prometheus_options); + } } buffer_flush(w->response.data); @@ -245,12 +278,17 @@ inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client case ALLMETRICS_PROMETHEUS: w->response.data->contenttype = CT_PROMETHEUS; - rrd_stats_api_v1_charts_allmetrics_prometheus(host, w->response.data); + rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(host, w->response.data, prometheus_server, prometheus_prefix, prometheus_options, help, types, names); + return 200; + + case ALLMETRICS_PROMETHEUS_ALL_HOSTS: + w->response.data->contenttype = CT_PROMETHEUS; + rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(host, w->response.data, prometheus_server, prometheus_prefix, prometheus_options, help, types, names); 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."); + buffer_strcat(w->response.data, "Which format? '" ALLMETRICS_FORMAT_SHELL "', '" ALLMETRICS_FORMAT_PROMETHEUS "', '" ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS "' and '" ALLMETRICS_FORMAT_JSON "' are currently supported."); return 400; } } diff --git a/src/web_client.c b/src/web_client.c index b5b25899f..7da080705 100644 --- a/src/web_client.c +++ b/src/web_client.c @@ -57,13 +57,7 @@ struct web_client *web_client_create(int listener) { w->mode = WEB_CLIENT_MODE_NORMAL; { - struct sockaddr *sadr; - socklen_t addrlen; - - sadr = (struct sockaddr*) &w->clientaddr; - addrlen = sizeof(w->clientaddr); - - w->ifd = accept4(listener, sadr, &addrlen, SOCK_NONBLOCK); + w->ifd = accept_socket(listener, SOCK_NONBLOCK, w->client_ip, sizeof(w->client_ip), w->client_port, sizeof(w->client_port)); if (w->ifd == -1) { error("%llu: Cannot accept new incoming connection.", w->id); freez(w); @@ -71,33 +65,6 @@ struct web_client *web_client_create(int listener) { } w->ofd = w->ifd; - if(getnameinfo(sadr, addrlen, w->client_ip, NI_MAXHOST, w->client_port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) != 0) { - error("Cannot getnameinfo() on received client connection."); - strncpyz(w->client_ip, "UNKNOWN", NI_MAXHOST); - strncpyz(w->client_port, "UNKNOWN", NI_MAXSERV); - } - w->client_ip[NI_MAXHOST] = '\0'; - w->client_port[NI_MAXSERV] = '\0'; - - switch(sadr->sa_family) { - case AF_INET: - debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd); - break; - - case AF_INET6: - if(strncmp(w->client_ip, "::ffff:", 7) == 0) { - memmove(w->client_ip, &w->client_ip[7], strlen(&w->client_ip[7]) + 1); - debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd); - } - else - debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv6 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd); - break; - - default: - debug(D_WEB_CLIENT_ACCESS, "%llu: New UNKNOWN web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd); - break; - } - int flag = 1; if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to enable TCP_NODELAY on socket.", w->id); @@ -388,8 +355,8 @@ int mysendfile(struct web_client *w, char *filename) { return 404; } } - if(fcntl(w->ifd, F_SETFL, O_NONBLOCK) < 0) - error("%llu: Cannot set O_NONBLOCK on file '%s'.", w->id, webfilename); + + sock_setnonblock(w->ifd); // pick a Content-Type for the file if(strstr(filename, ".html") != NULL) w->response.data->contenttype = CT_TEXT_HTML; @@ -995,13 +962,22 @@ static inline void web_client_send_http_header(struct web_client *w) { web_client_crock_socket(w); - ssize_t bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0); + size_t count = 0; + ssize_t bytes; + while((bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0)) == -1) { + count++; + + if(count > 100 || (errno != EAGAIN && errno != EWOULDBLOCK)) { + error("Cannot send HTTP headers to web client."); + break; + } + } + 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 + error("HTTP headers failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client." , buffer_strlen(w->response.header_output) , bytes); diff --git a/src/web_client.h b/src/web_client.h index 70c5b1ff0..617917df0 100644 --- a/src/web_client.h +++ b/src/web_client.h @@ -83,7 +83,6 @@ struct web_client { char cookie2[COOKIE_MAX+1]; char origin[ORIGIN_MAX+1]; - struct sockaddr_storage clientaddr; struct response response; size_t stats_received_bytes; diff --git a/src/web_server.c b/src/web_server.c index 593a82a57..491cd11aa 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -1,15 +1,14 @@ #include "common.h" -int listen_backlog = LISTEN_BACKLOG; -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; +static LISTEN_SOCKETS api_sockets = { + .config_section = CONFIG_SECTION_WEB, + .default_bind_to = "*", + .default_port = API_LISTEN_PORT, + .backlog = API_LISTEN_BACKLOG +}; WEB_SERVER_MODE web_server_mode = WEB_SERVER_MODE_MULTI_THREADED; -static int shown_server_socket_error = 0; - #ifdef NETDATA_INTERNAL_CHECKS static void log_allocations(void) { @@ -47,42 +46,7 @@ static void log_allocations(void) } #endif /* NETDATA_INTERNAL_CHECKS */ -#ifndef HAVE_ACCEPT4 -int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags) { - int fd = accept(sock, addr, addrlen); - int newflags = 0; - - if (fd < 0) return fd; - - if (flags & SOCK_NONBLOCK) { - newflags |= O_NONBLOCK; - flags &= ~SOCK_NONBLOCK; - } - -#ifdef SOCK_CLOEXEC -#ifdef O_CLOEXEC - if (flags & SOCK_CLOEXEC) { - newflags |= O_CLOEXEC; - flags &= ~SOCK_CLOEXEC; - } -#endif -#endif - - if (flags) { - errno = -EINVAL; - return -1; - } - - if (fcntl(fd, F_SETFL, newflags) < 0) { - int saved_errno = errno; - close(fd); - errno = saved_errno; - return -1; - } - - return fd; -} -#endif +// -------------------------------------------------------------------------------------- WEB_SERVER_MODE web_server_mode_id(const char *mode) { if(!strcmp(mode, "none")) @@ -107,277 +71,15 @@ const char *web_server_mode_name(WEB_SERVER_MODE id) { } } -int create_listen_socket4(const char *ip, int port, int listen_backlog) { - int sock; - int sockopt = 1; - - debug(D_LISTENER, "IPv4 creating new listening socket on ip '%s' port %d", ip, port); - - sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock < 0) { - error("IPv4 socket() on ip '%s' port %d failed.", ip, port); - shown_server_socket_error = 1; - return -1; - } - - /* avoid "address already in use" */ - if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt)) != 0) - error("Cannot set SO_REUSEADDR on ip '%s' port's %d.", ip, port); - - struct sockaddr_in name; - memset(&name, 0, sizeof(struct sockaddr_in)); - name.sin_family = AF_INET; - name.sin_port = htons (port); - - int ret = inet_pton(AF_INET, ip, (void *)&name.sin_addr.s_addr); - if(ret != 1) { - error("Failed to convert IP '%s' to a valid IPv4 address.", ip); - shown_server_socket_error = 1; - close(sock); - return -1; - } - - if(bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { - close(sock); - error("IPv4 bind() on ip '%s' port %d failed.", ip, port); - shown_server_socket_error = 1; - return -1; - } - - if(listen(sock, listen_backlog) < 0) { - close(sock); - error("IPv4 listen() on ip '%s' port %d failed.", ip, port); - shown_server_socket_error = 1; - return -1; - } - - debug(D_LISTENER, "Listening on IPv4 ip '%s' port %d", ip, port); - return sock; -} - -int create_listen_socket6(const char *ip, int port, int listen_backlog) { - int sock = -1; - int sockopt = 1; - int ipv6only = 1; - - debug(D_LISTENER, "IPv6 creating new listening socket on ip '%s' port %d", ip, port); - - sock = socket(AF_INET6, SOCK_STREAM, 0); - if (sock < 0) { - error("IPv6 socket() on ip '%s' port %d failed.", ip, port); - shown_server_socket_error = 1; - return -1; - } - - /* avoid "address already in use" */ - if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt)) != 0) - error("Cannot set SO_REUSEADDR on ip '%s' port's %d.", ip, port); - - /* IPv6 only */ - if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&ipv6only, sizeof(ipv6only)) != 0) - error("Cannot set IPV6_V6ONLY on ip '%s' port's %d.", ip, port); - - struct sockaddr_in6 name; - memset(&name, 0, sizeof(struct sockaddr_in6)); - name.sin6_family = AF_INET6; - name.sin6_port = htons ((uint16_t) port); - - int ret = inet_pton(AF_INET6, ip, (void *)&name.sin6_addr.s6_addr); - if(ret != 1) { - error("Failed to convert IP '%s' to a valid IPv6 address.", ip); - shown_server_socket_error = 1; - close(sock); - return -1; - } - - name.sin6_scope_id = 0; - - if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) { - close(sock); - error("IPv6 bind() on ip '%s' port %d failed.", ip, port); - shown_server_socket_error = 1; - return -1; - } - - if (listen(sock, listen_backlog) < 0) { - close(sock); - error("IPv6 listen() on ip '%s' port %d failed.", ip, port); - shown_server_socket_error = 1; - return -1; - } - - debug(D_LISTENER, "Listening on IPv6 ip '%s' port %d", ip, port); - return sock; -} - -static inline int add_listen_socket(int fd, const char *ip, int port) { - if(listen_fds_count >= MAX_LISTEN_FDS) { - error("Too many listening sockets. Failed to add listening socket at ip '%s' port %d", ip, port); - shown_server_socket_error = 1; - close(fd); - return -1; - } - - listen_fds[listen_fds_count] = fd; - - char buffer[100 + 1]; - snprintfz(buffer, 100, "[%s]:%d", ip, port); - listen_fds_names[listen_fds_count] = strdupz(buffer); - - listen_fds_count++; - return 0; -} - -int is_listen_socket(int fd) { - size_t i; - for(i = 0; i < listen_fds_count ;i++) - if(listen_fds[i] == fd) return 1; - - return 0; -} - -static inline void close_listen_sockets(void) { - size_t i; - for(i = 0; i < listen_fds_count ;i++) { - close(listen_fds[i]); - listen_fds[i] = -1; - - freez(listen_fds_names[i]); - listen_fds_names[i] = NULL; - } - - listen_fds_count = 0; -} - -static inline int bind_to_one(const char *definition, int default_port, int listen_backlog) { - int added = 0; - struct addrinfo hints; - struct addrinfo *result = NULL, *rp = NULL; - - char buffer[strlen(definition) + 1]; - strcpy(buffer, definition); - - char buffer2[10 + 1]; - snprintfz(buffer2, 10, "%d", default_port); - - char *ip = buffer, *port = buffer2; - - char *e = ip; - if(*e == '[') { - e = ++ip; - while(*e && *e != ']') e++; - if(*e == ']') { - *e = '\0'; - e++; - } - } - else { - while(*e && *e != ':') e++; - } - - if(*e == ':') { - port = e + 1; - *e = '\0'; - } - - if(!*ip || *ip == '*' || !strcmp(ip, "any") || !strcmp(ip, "all")) - ip = NULL; - if(!*port) - port = buffer2; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ - hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ - hints.ai_protocol = 0; /* Any protocol */ - hints.ai_canonname = NULL; - hints.ai_addr = NULL; - hints.ai_next = NULL; - - int r = getaddrinfo(ip, port, &hints, &result); - if (r != 0) { - error("getaddrinfo('%s', '%s'): %s\n", ip, port, gai_strerror(r)); - return -1; - } - - for (rp = result; rp != NULL; rp = rp->ai_next) { - int fd = -1; - - char rip[INET_ADDRSTRLEN + INET6_ADDRSTRLEN] = "INVALID"; - int rport = default_port; - - switch (rp->ai_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in *) rp->ai_addr; - inet_ntop(AF_INET, &sin->sin_addr, rip, INET_ADDRSTRLEN); - rport = ntohs(sin->sin_port); - fd = create_listen_socket4(rip, rport, listen_backlog); - break; - } - - case AF_INET6: { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) rp->ai_addr; - inet_ntop(AF_INET6, &sin6->sin6_addr, rip, INET6_ADDRSTRLEN); - rport = ntohs(sin6->sin6_port); - fd = create_listen_socket6(rip, rport, listen_backlog); - break; - } - } - - if (fd == -1) - error("Cannot bind to ip '%s', port %d", rip, rport); - else { - add_listen_socket(fd, rip, rport); - added++; - } - } - - freeaddrinfo(result); - - return added; -} - -int create_listen_sockets(void) { - shown_server_socket_error = 0; - - listen_backlog = (int) config_get_number(CONFIG_SECTION_WEB, "listen backlog", LISTEN_BACKLOG); - - 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(CONFIG_SECTION_WEB, "default port", LISTEN_PORT); - } - debug(D_OPTIONS, "Default listen port set to %d.", listen_port); - - char *s = config_get(CONFIG_SECTION_WEB, "bind to", "*"); - while(*s) { - char *e = s; - - // skip separators, moving both s(tart) and e(nd) - while(isspace(*e) || *e == ',') s = ++e; - - // move e(nd) to the first separator - while(*e && !isspace(*e) && *e != ',') e++; - - // is there anything? - if(!*s || s == e) break; - - char buf[e - s + 1]; - strncpyz(buf, s, e - s); - bind_to_one(buf, listen_port, listen_backlog); +// -------------------------------------------------------------------------------------- - s = e; - } +int api_listen_sockets_setup(void) { + int socks = listen_sockets_setup(&api_sockets); - if(!listen_fds_count) - fatal("Cannot listen on any socket. Exiting..."); - else if(shown_server_socket_error) { - size_t i; - for(i = 0; i < listen_fds_count ;i++) - info("Listen socket %s opened.", listen_fds_names[i]); - } + if(!socks) + fatal("LISTENER: Cannot listen on any API socket. Exiting..."); - return (int)listen_fds_count; + return socks; } // -------------------------------------------------------------------------------------- @@ -422,25 +124,25 @@ void *socket_listen_main_multi_threaded(void *ptr) { if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); - if(!listen_fds_count) + if(!api_sockets.opened) fatal("LISTENER: No sockets to listen to."); - struct pollfd *fds = callocz(sizeof(struct pollfd), listen_fds_count); + struct pollfd *fds = callocz(sizeof(struct pollfd), api_sockets.opened); size_t i; - for(i = 0; i < listen_fds_count ;i++) { - fds[i].fd = listen_fds[i]; + for(i = 0; i < api_sockets.opened ;i++) { + fds[i].fd = api_sockets.fds[i]; fds[i].events = POLLIN; fds[i].revents = 0; - info("Listening on '%s'", (listen_fds_names[i])?listen_fds_names[i]:"UNKNOWN"); + info("Listening on '%s'", (api_sockets.fds_names[i])?api_sockets.fds_names[i]:"UNKNOWN"); } int timeout = 10 * 1000; for(;;) { // debug(D_WEB_CLIENT, "LISTENER: Waiting..."); - retval = poll(fds, listen_fds_count, timeout); + retval = poll(fds, api_sockets.opened, timeout); if(unlikely(retval == -1)) { error("LISTENER: poll() failed."); @@ -453,7 +155,7 @@ void *socket_listen_main_multi_threaded(void *ptr) { continue; } - for(i = 0 ; i < listen_fds_count ; i++) { + for(i = 0 ; i < api_sockets.opened ; i++) { short int revents = fds[i].revents; // check for new incoming connections @@ -486,7 +188,7 @@ void *socket_listen_main_multi_threaded(void *ptr) { } debug(D_WEB_CLIENT, "LISTENER: exit!"); - close_listen_sockets(); + listen_sockets_close(&api_sockets); freez(fds); @@ -555,7 +257,7 @@ void *socket_listen_main_single_threaded(void *ptr) { if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); - if(!listen_fds_count) + if(!api_sockets.opened) fatal("LISTENER: no listen sockets available."); size_t i; @@ -568,16 +270,16 @@ void *socket_listen_main_single_threaded(void *ptr) { FD_ZERO (&efds); int fdmax = 0; - for(i = 0; i < listen_fds_count ; i++) { - if (listen_fds[i] < 0 || listen_fds[i] >= FD_SETSIZE) - fatal("LISTENER: Listen socket %d is not ready, or invalid.", listen_fds[i]); + for(i = 0; i < api_sockets.opened ; i++) { + if (api_sockets.fds[i] < 0 || api_sockets.fds[i] >= FD_SETSIZE) + fatal("LISTENER: Listen socket %d is not ready, or invalid.", api_sockets.fds[i]); - info("Listening on '%s'", (listen_fds_names[i])?listen_fds_names[i]:"UNKNOWN"); + info("Listening on '%s'", (api_sockets.fds_names[i])?api_sockets.fds_names[i]:"UNKNOWN"); - FD_SET(listen_fds[i], &ifds); - FD_SET(listen_fds[i], &efds); - if(fdmax < listen_fds[i]) - fdmax = listen_fds[i]; + FD_SET(api_sockets.fds[i], &ifds); + FD_SET(api_sockets.fds[i], &efds); + if(fdmax < api_sockets.fds[i]) + fdmax = api_sockets.fds[i]; } for(;;) { @@ -596,10 +298,10 @@ void *socket_listen_main_single_threaded(void *ptr) { else if(likely(retval)) { debug(D_WEB_CLIENT_ACCESS, "LISTENER: got something."); - for(i = 0; i < listen_fds_count ; i++) { - if (FD_ISSET(listen_fds[i], &rifds)) { + for(i = 0; i < api_sockets.opened ; i++) { + if (FD_ISSET(api_sockets.fds[i], &rifds)) { debug(D_WEB_CLIENT_ACCESS, "LISTENER: new connection."); - w = web_client_create(listen_fds[i]); + w = web_client_create(api_sockets.fds[i]); if (single_threaded_link_client(w, &ifds, &ofds, &ifds, &fdmax) != 0) { web_client_free(w); } @@ -658,7 +360,7 @@ void *socket_listen_main_single_threaded(void *ptr) { } debug(D_WEB_CLIENT, "LISTENER: exit!"); - close_listen_sockets(); + listen_sockets_close(&api_sockets); static_thread->enabled = 0; pthread_exit(NULL); diff --git a/src/web_server.h b/src/web_server.h index 41dcfcf09..aa293695d 100644 --- a/src/web_server.h +++ b/src/web_server.h @@ -6,11 +6,12 @@ #define WEB_PATH_DATASOURCE "datasource" #define WEB_PATH_GRAPH "graph" -#define LISTEN_PORT 19999 -#define LISTEN_BACKLOG 100 +#ifndef API_LISTEN_PORT +#define API_LISTEN_PORT 19999 +#endif -#ifndef MAX_LISTEN_FDS -#define MAX_LISTEN_FDS 100 +#ifndef API_LISTEN_BACKLOG +#define API_LISTEN_BACKLOG 4096 #endif typedef enum web_server_mode { @@ -24,23 +25,8 @@ 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); -extern int create_listen_sockets(void); -extern int is_listen_socket(int fd); - -#ifndef HAVE_ACCEPT4 -extern int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags); - -#ifndef SOCK_NONBLOCK -#define SOCK_NONBLOCK 00004000 -#endif /* #ifndef SOCK_NONBLOCK */ - -#ifndef SOCK_CLOEXEC -#define SOCK_CLOEXEC 02000000 -#endif /* #ifndef SOCK_CLOEXEC */ - -#endif /* #ifndef HAVE_ACCEPT4 */ +extern int api_listen_sockets_setup(void); #endif /* NETDATA_WEB_SERVER_H */ diff --git a/src/zfs_common.c b/src/zfs_common.c new file mode 100644 index 000000000..7fa05b03e --- /dev/null +++ b/src/zfs_common.c @@ -0,0 +1,677 @@ +#include "common.h" +#include "zfs_common.h" + +extern struct arcstats arcstats; + +void generate_charts_arcstats(int update_every) { + + // ARC reads + unsigned long long aread = arcstats.hits + arcstats.misses; + + // Demand reads + unsigned long long dhit = arcstats.demand_data_hits + arcstats.demand_metadata_hits; + unsigned long long dmiss = arcstats.demand_data_misses + arcstats.demand_metadata_misses; + unsigned long long dread = dhit + dmiss; + + // Prefetch reads + unsigned long long phit = arcstats.prefetch_data_hits + arcstats.prefetch_metadata_hits; + unsigned long long pmiss = arcstats.prefetch_data_misses + arcstats.prefetch_metadata_misses; + unsigned long long pread = phit + pmiss; + + // Metadata reads + unsigned long long mhit = arcstats.prefetch_metadata_hits + arcstats.demand_metadata_hits; + unsigned long long mmiss = arcstats.prefetch_metadata_misses + arcstats.demand_metadata_misses; + unsigned long long mread = mhit + mmiss; + + // l2 reads + unsigned long long l2hit = arcstats.l2_hits + arcstats.l2_misses; + unsigned long long l2miss = arcstats.prefetch_metadata_misses + arcstats.demand_metadata_misses; + unsigned long long l2read = l2hit + l2miss; + + // -------------------------------------------------------------------- + + { + static RRDSET *st_arc_size = NULL; + static RRDDIM *rd_arc_size = NULL; + static RRDDIM *rd_arc_target_size = NULL; + static RRDDIM *rd_arc_target_min_size = NULL; + static RRDDIM *rd_arc_target_max_size = NULL; + + if (unlikely(!st_arc_size)) { + st_arc_size = rrdset_create_localhost( + "zfs" + , "arc_size" + , NULL + , ZFS_FAMILY_SIZE + , NULL + , "ZFS ARC Size" + , "MB" + , 2000 + , update_every + , RRDSET_TYPE_AREA + ); + + rd_arc_size = rrddim_add(st_arc_size, "size", "arcsz", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rd_arc_target_size = rrddim_add(st_arc_size, "target", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rd_arc_target_min_size = rrddim_add(st_arc_size, "min", "min (hard limit)", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rd_arc_target_max_size = rrddim_add(st_arc_size, "max", "max (high water)", 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + } + else + rrdset_next(st_arc_size); + + rrddim_set_by_pointer(st_arc_size, rd_arc_size, arcstats.size); + rrddim_set_by_pointer(st_arc_size, rd_arc_target_size, arcstats.c); + rrddim_set_by_pointer(st_arc_size, rd_arc_target_min_size, arcstats.c_min); + rrddim_set_by_pointer(st_arc_size, rd_arc_target_max_size, arcstats.c_max); + rrdset_done(st_arc_size); + } + + // -------------------------------------------------------------------- + + if(likely(l2exist)) { + static RRDSET *st_l2_size = NULL; + static RRDDIM *rd_l2_size = NULL; + static RRDDIM *rd_l2_asize = NULL; + + if (unlikely(!st_l2_size)) { + st_l2_size = rrdset_create_localhost( + "zfs" + , "l2_size" + , NULL + , ZFS_FAMILY_SIZE + , NULL + , "ZFS L2 ARC Size" + , "MB" + , 2000 + , update_every + , RRDSET_TYPE_AREA + ); + + rd_l2_asize = rrddim_add(st_l2_size, "actual", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + rd_l2_size = rrddim_add(st_l2_size, "size", NULL, 1, 1024 * 1024, RRD_ALGORITHM_ABSOLUTE); + } + else + rrdset_next(st_l2_size); + + rrddim_set_by_pointer(st_l2_size, rd_l2_size, arcstats.l2_size); + rrddim_set_by_pointer(st_l2_size, rd_l2_asize, arcstats.l2_asize); + rrdset_done(st_l2_size); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_reads = NULL; + static RRDDIM *rd_aread = NULL; + static RRDDIM *rd_dread = NULL; + static RRDDIM *rd_pread = NULL; + static RRDDIM *rd_mread = NULL; + static RRDDIM *rd_l2read = NULL; + + if (unlikely(!st_reads)) { + st_reads = rrdset_create_localhost( + "zfs" + , "reads" + , NULL + , ZFS_FAMILY_ACCESSES + , NULL + , "ZFS Reads" + , "reads/s" + , 2010 + , update_every + , RRDSET_TYPE_AREA + ); + + rd_aread = rrddim_add(st_reads, "areads", "arc", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_dread = rrddim_add(st_reads, "dreads", "demand", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_pread = rrddim_add(st_reads, "preads", "prefetch", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_mread = rrddim_add(st_reads, "mreads", "metadata", 1, 1, RRD_ALGORITHM_INCREMENTAL); + + if(l2exist) + rd_l2read = rrddim_add(st_reads, "l2reads", "l2", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else + rrdset_next(st_reads); + + rrddim_set_by_pointer(st_reads, rd_aread, aread); + rrddim_set_by_pointer(st_reads, rd_dread, dread); + rrddim_set_by_pointer(st_reads, rd_pread, pread); + rrddim_set_by_pointer(st_reads, rd_mread, mread); + + if(l2exist) + rrddim_set_by_pointer(st_reads, rd_l2read, l2read); + + rrdset_done(st_reads); + } + + // -------------------------------------------------------------------- + + if(likely(l2exist)) { + static RRDSET *st_l2bytes = NULL; + static RRDDIM *rd_l2_read_bytes = NULL; + static RRDDIM *rd_l2_write_bytes = NULL; + + if (unlikely(!st_l2bytes)) { + st_l2bytes = rrdset_create_localhost( + "zfs" + , "bytes" + , NULL + , ZFS_FAMILY_ACCESSES + , NULL + , "ZFS ARC L2 Read/Write Rate" + , "kilobytes/s" + , 2200 + , update_every + , RRDSET_TYPE_AREA + ); + + rd_l2_read_bytes = rrddim_add(st_l2bytes, "read", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL); + rd_l2_write_bytes = rrddim_add(st_l2bytes, "write", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL); + } + else + rrdset_next(st_l2bytes); + + rrddim_set_by_pointer(st_l2bytes, rd_l2_read_bytes, arcstats.l2_read_bytes); + rrddim_set_by_pointer(st_l2bytes, rd_l2_write_bytes, arcstats.l2_write_bytes); + rrdset_done(st_l2bytes); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_ahits = NULL; + static RRDDIM *rd_ahits = NULL; + static RRDDIM *rd_amisses = NULL; + + if (unlikely(!st_ahits)) { + st_ahits = rrdset_create_localhost( + "zfs" + , "hits" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS ARC Hits" + , "percentage" + , 2020 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_ahits = rrddim_add(st_ahits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_amisses = rrddim_add(st_ahits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + } + else + rrdset_next(st_ahits); + + rrddim_set_by_pointer(st_ahits, rd_ahits, arcstats.hits); + rrddim_set_by_pointer(st_ahits, rd_amisses, arcstats.misses); + rrdset_done(st_ahits); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_dhits = NULL; + static RRDDIM *rd_dhits = NULL; + static RRDDIM *rd_dmisses = NULL; + + if (unlikely(!st_dhits)) { + st_dhits = rrdset_create_localhost( + "zfs" + , "dhits" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Demand Hits" + , "percentage" + , 2030 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_dhits = rrddim_add(st_dhits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_dmisses = rrddim_add(st_dhits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + } + else + rrdset_next(st_dhits); + + rrddim_set_by_pointer(st_dhits, rd_dhits, dhit); + rrddim_set_by_pointer(st_dhits, rd_dmisses, dmiss); + rrdset_done(st_dhits); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_phits = NULL; + static RRDDIM *rd_phits = NULL; + static RRDDIM *rd_pmisses = NULL; + + if (unlikely(!st_phits)) { + st_phits = rrdset_create_localhost( + "zfs" + , "phits" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Prefetch Hits" + , "percentage" + , 2040 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_phits = rrddim_add(st_phits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_pmisses = rrddim_add(st_phits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + } + else + rrdset_next(st_phits); + + rrddim_set_by_pointer(st_phits, rd_phits, phit); + rrddim_set_by_pointer(st_phits, rd_pmisses, pmiss); + rrdset_done(st_phits); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_mhits = NULL; + static RRDDIM *rd_mhits = NULL; + static RRDDIM *rd_mmisses = NULL; + + if (unlikely(!st_mhits)) { + st_mhits = rrdset_create_localhost( + "zfs" + , "mhits" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Metadata Hits" + , "percentage" + , 2050 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_mhits = rrddim_add(st_mhits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_mmisses = rrddim_add(st_mhits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + } + else + rrdset_next(st_mhits); + + rrddim_set_by_pointer(st_mhits, rd_mhits, mhit); + rrddim_set_by_pointer(st_mhits, rd_mmisses, mmiss); + rrdset_done(st_mhits); + } + + // -------------------------------------------------------------------- + + if(likely(l2exist)) { + static RRDSET *st_l2hits = NULL; + static RRDDIM *rd_l2hits = NULL; + static RRDDIM *rd_l2misses = NULL; + + if (unlikely(!st_l2hits)) { + st_l2hits = rrdset_create_localhost( + "zfs" + , "l2hits" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS L2 Hits" + , "percentage" + , 2060 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_l2hits = rrddim_add(st_l2hits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_l2misses = rrddim_add(st_l2hits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + } + else + rrdset_next(st_l2hits); + + rrddim_set_by_pointer(st_l2hits, rd_l2hits, l2hit); + rrddim_set_by_pointer(st_l2hits, rd_l2misses, l2miss); + rrdset_done(st_l2hits); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_list_hits = NULL; + static RRDDIM *rd_mfu = NULL; + static RRDDIM *rd_mru = NULL; + static RRDDIM *rd_mfug = NULL; + static RRDDIM *rd_mrug = NULL; + + if (unlikely(!st_list_hits)) { + st_list_hits = rrdset_create_localhost( + "zfs" + , "list_hits" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS List Hits" + , "hits/s" + , 2100 + , update_every + , RRDSET_TYPE_AREA + ); + + rd_mfu = rrddim_add(st_list_hits, "mfu", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_mfug = rrddim_add(st_list_hits, "mfug", "mfu ghost", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_mru = rrddim_add(st_list_hits, "mru", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_mrug = rrddim_add(st_list_hits, "mrug", "mru ghost", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else + rrdset_next(st_list_hits); + + rrddim_set_by_pointer(st_list_hits, rd_mfu, arcstats.mfu_hits); + rrddim_set_by_pointer(st_list_hits, rd_mru, arcstats.mru_hits); + rrddim_set_by_pointer(st_list_hits, rd_mfug, arcstats.mfu_ghost_hits); + rrddim_set_by_pointer(st_list_hits, rd_mrug, arcstats.mru_ghost_hits); + rrdset_done(st_list_hits); + } +} + +void generate_charts_arc_summary(int update_every) { + unsigned long long arc_accesses_total = arcstats.hits + arcstats.misses; + unsigned long long real_hits = arcstats.mfu_hits + arcstats.mru_hits; + unsigned long long real_misses = arc_accesses_total - real_hits; + + //unsigned long long anon_hits = arcstats.hits - (arcstats.mfu_hits + arcstats.mru_hits + arcstats.mfu_ghost_hits + arcstats.mru_ghost_hits); + + unsigned long long arc_size = arcstats.size; + unsigned long long mru_size = arcstats.p; + //unsigned long long target_min_size = arcstats.c_min; + //unsigned long long target_max_size = arcstats.c_max; + unsigned long long target_size = arcstats.c; + //unsigned long long target_size_ratio = (target_max_size / target_min_size); + + unsigned long long mfu_size; + if(arc_size > target_size) + mfu_size = arc_size - mru_size; + else + mfu_size = target_size - mru_size; + + // -------------------------------------------------------------------- + + { + static RRDSET *st_arc_size_breakdown = NULL; + static RRDDIM *rd_most_recent = NULL; + static RRDDIM *rd_most_frequent = NULL; + + if (unlikely(!st_arc_size_breakdown)) { + st_arc_size_breakdown = rrdset_create_localhost( + "zfs" + , "arc_size_breakdown" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS ARC Size Breakdown" + , "percentage" + , 2020 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_most_recent = rrddim_add(st_arc_size_breakdown, "recent", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL); + rd_most_frequent = rrddim_add(st_arc_size_breakdown, "frequent", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL); + } + else + rrdset_next(st_arc_size_breakdown); + + rrddim_set_by_pointer(st_arc_size_breakdown, rd_most_recent, mru_size); + rrddim_set_by_pointer(st_arc_size_breakdown, rd_most_frequent, mfu_size); + rrdset_done(st_arc_size_breakdown); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_memory = NULL; +#ifndef __FreeBSD__ + static RRDDIM *rd_direct = NULL; +#endif + static RRDDIM *rd_throttled = NULL; +#ifndef __FreeBSD__ + static RRDDIM *rd_indirect = NULL; +#endif + + if (unlikely(!st_memory)) { + st_memory = rrdset_create_localhost( + "zfs" + , "memory_ops" + , NULL + , ZFS_FAMILY_OPERATIONS + , NULL + , "ZFS Memory Operations" + , "operations/s" + , 2023 + , update_every + , RRDSET_TYPE_LINE + ); + +#ifndef __FreeBSD__ + rd_direct = rrddim_add(st_memory, "direct", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); +#endif + rd_throttled = rrddim_add(st_memory, "throttled", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); +#ifndef __FreeBSD__ + rd_indirect = rrddim_add(st_memory, "indirect", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); +#endif + } + else + rrdset_next(st_memory); + +#ifndef __FreeBSD__ + rrddim_set_by_pointer(st_memory, rd_direct, arcstats.memory_direct_count); +#endif + rrddim_set_by_pointer(st_memory, rd_throttled, arcstats.memory_throttle_count); +#ifndef __FreeBSD__ + rrddim_set_by_pointer(st_memory, rd_indirect, arcstats.memory_indirect_count); +#endif + rrdset_done(st_memory); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_important_ops = NULL; + static RRDDIM *rd_deleted = NULL; + static RRDDIM *rd_mutex_misses = NULL; + static RRDDIM *rd_evict_skips = NULL; + static RRDDIM *rd_hash_collisions = NULL; + + if (unlikely(!st_important_ops)) { + st_important_ops = rrdset_create_localhost( + "zfs" + , "important_ops" + , NULL + , ZFS_FAMILY_OPERATIONS + , NULL + , "ZFS Important Operations" + , "operations/s" + , 2022 + , update_every + , RRDSET_TYPE_LINE + ); + + rd_evict_skips = rrddim_add(st_important_ops, "eskip", "evict skip", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_deleted = rrddim_add(st_important_ops, "deleted", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_mutex_misses = rrddim_add(st_important_ops, "mtxmis", "mutex miss", 1, 1, RRD_ALGORITHM_INCREMENTAL); + rd_hash_collisions = rrddim_add(st_important_ops, "hash_collisions", "hash collisions", 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + else + rrdset_next(st_important_ops); + + rrddim_set_by_pointer(st_important_ops, rd_deleted, arcstats.deleted); + rrddim_set_by_pointer(st_important_ops, rd_evict_skips, arcstats.evict_skip); + rrddim_set_by_pointer(st_important_ops, rd_mutex_misses, arcstats.mutex_miss); + rrddim_set_by_pointer(st_important_ops, rd_hash_collisions, arcstats.hash_collisions); + rrdset_done(st_important_ops); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_actual_hits = NULL; + static RRDDIM *rd_actual_hits = NULL; + static RRDDIM *rd_actual_misses = NULL; + + if (unlikely(!st_actual_hits)) { + st_actual_hits = rrdset_create_localhost( + "zfs" + , "actual_hits" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Actual Cache Hits" + , "percentage" + , 2019 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_actual_hits = rrddim_add(st_actual_hits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_actual_misses = rrddim_add(st_actual_hits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + } + else + rrdset_next(st_actual_hits); + + rrddim_set_by_pointer(st_actual_hits, rd_actual_hits, real_hits); + rrddim_set_by_pointer(st_actual_hits, rd_actual_misses, real_misses); + rrdset_done(st_actual_hits); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_demand_data_hits = NULL; + static RRDDIM *rd_demand_data_hits = NULL; + static RRDDIM *rd_demand_data_misses = NULL; + + if (unlikely(!st_demand_data_hits)) { + st_demand_data_hits = rrdset_create_localhost( + "zfs" + , "demand_data_hits" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Data Demand Efficiency" + , "percentage" + , 2031 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_demand_data_hits = rrddim_add(st_demand_data_hits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_demand_data_misses = rrddim_add(st_demand_data_hits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + } + else + rrdset_next(st_demand_data_hits); + + rrddim_set_by_pointer(st_demand_data_hits, rd_demand_data_hits, arcstats.demand_data_hits); + rrddim_set_by_pointer(st_demand_data_hits, rd_demand_data_misses, arcstats.demand_data_misses); + rrdset_done(st_demand_data_hits); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_prefetch_data_hits = NULL; + static RRDDIM *rd_prefetch_data_hits = NULL; + static RRDDIM *rd_prefetch_data_misses = NULL; + + if (unlikely(!st_prefetch_data_hits)) { + st_prefetch_data_hits = rrdset_create_localhost( + "zfs" + , "prefetch_data_hits" + , NULL + , ZFS_FAMILY_EFFICIENCY + , NULL + , "ZFS Data Prefetch Efficiency" + , "percentage" + , 2032 + , update_every + , RRDSET_TYPE_STACKED + ); + + rd_prefetch_data_hits = rrddim_add(st_prefetch_data_hits, "hits", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + rd_prefetch_data_misses = rrddim_add(st_prefetch_data_hits, "misses", NULL, 1, 1, RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL); + } + else + rrdset_next(st_prefetch_data_hits); + + rrddim_set_by_pointer(st_prefetch_data_hits, rd_prefetch_data_hits, arcstats.prefetch_data_hits); + rrddim_set_by_pointer(st_prefetch_data_hits, rd_prefetch_data_misses, arcstats.prefetch_data_misses); + rrdset_done(st_prefetch_data_hits); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_hash_elements = NULL; + static RRDDIM *rd_hash_elements_current = NULL; + static RRDDIM *rd_hash_elements_max = NULL; + + if (unlikely(!st_hash_elements)) { + st_hash_elements = rrdset_create_localhost( + "zfs" + , "hash_elements" + , NULL + , ZFS_FAMILY_HASH + , NULL + , "ZFS ARC Hash Elements" + , "elements" + , 2300 + , update_every + , RRDSET_TYPE_LINE + ); + + rd_hash_elements_current = rrddim_add(st_hash_elements, "current", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_hash_elements_max = rrddim_add(st_hash_elements, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + else + rrdset_next(st_hash_elements); + + rrddim_set_by_pointer(st_hash_elements, rd_hash_elements_current, arcstats.hash_elements); + rrddim_set_by_pointer(st_hash_elements, rd_hash_elements_max, arcstats.hash_elements_max); + rrdset_done(st_hash_elements); + } + + // -------------------------------------------------------------------- + + { + static RRDSET *st_hash_chains = NULL; + static RRDDIM *rd_hash_chains_current = NULL; + static RRDDIM *rd_hash_chains_max = NULL; + + if (unlikely(!st_hash_chains)) { + st_hash_chains = rrdset_create_localhost( + "zfs" + , "hash_chains" + , NULL + , ZFS_FAMILY_HASH + , NULL + , "ZFS ARC Hash Chains" + , "chains" + , 2310 + , update_every + , RRDSET_TYPE_LINE + ); + + rd_hash_chains_current = rrddim_add(st_hash_chains, "current", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + rd_hash_chains_max = rrddim_add(st_hash_chains, "max", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + else + rrdset_next(st_hash_chains); + + rrddim_set_by_pointer(st_hash_chains, rd_hash_chains_current, arcstats.hash_chains); + rrddim_set_by_pointer(st_hash_chains, rd_hash_chains_max, arcstats.hash_chain_max); + rrdset_done(st_hash_chains); + } + + // -------------------------------------------------------------------- + +} \ No newline at end of file diff --git a/src/zfs_common.h b/src/zfs_common.h new file mode 100644 index 000000000..9d3aa7dfb --- /dev/null +++ b/src/zfs_common.h @@ -0,0 +1,109 @@ +#ifndef NETDATA_ZFS_COMMON_H +#define NETDATA_ZFS_COMMON_H + +#define ZFS_FAMILY_SIZE "size" +#define ZFS_FAMILY_EFFICIENCY "efficiency" +#define ZFS_FAMILY_ACCESSES "accesses" +#define ZFS_FAMILY_OPERATIONS "operations" +#define ZFS_FAMILY_HASH "hashes" + +struct arcstats { + unsigned long long hits; + unsigned long long misses; + unsigned long long demand_data_hits; + unsigned long long demand_data_misses; + unsigned long long demand_metadata_hits; + unsigned long long demand_metadata_misses; + unsigned long long prefetch_data_hits; + unsigned long long prefetch_data_misses; + unsigned long long prefetch_metadata_hits; + unsigned long long prefetch_metadata_misses; + unsigned long long mru_hits; + unsigned long long mru_ghost_hits; + unsigned long long mfu_hits; + unsigned long long mfu_ghost_hits; + unsigned long long deleted; + unsigned long long mutex_miss; + unsigned long long evict_skip; + unsigned long long evict_not_enough; + unsigned long long evict_l2_cached; + unsigned long long evict_l2_eligible; + unsigned long long evict_l2_ineligible; + unsigned long long evict_l2_skip; + unsigned long long hash_elements; + unsigned long long hash_elements_max; + unsigned long long hash_collisions; + unsigned long long hash_chains; + unsigned long long hash_chain_max; + unsigned long long p; + unsigned long long c; + unsigned long long c_min; + unsigned long long c_max; + unsigned long long size; + unsigned long long hdr_size; + unsigned long long data_size; + unsigned long long metadata_size; + unsigned long long other_size; + unsigned long long anon_size; + unsigned long long anon_evictable_data; + unsigned long long anon_evictable_metadata; + unsigned long long mru_size; + unsigned long long mru_evictable_data; + unsigned long long mru_evictable_metadata; + unsigned long long mru_ghost_size; + unsigned long long mru_ghost_evictable_data; + unsigned long long mru_ghost_evictable_metadata; + unsigned long long mfu_size; + unsigned long long mfu_evictable_data; + unsigned long long mfu_evictable_metadata; + unsigned long long mfu_ghost_size; + unsigned long long mfu_ghost_evictable_data; + unsigned long long mfu_ghost_evictable_metadata; + unsigned long long l2_hits; + unsigned long long l2_misses; + unsigned long long l2_feeds; + unsigned long long l2_rw_clash; + unsigned long long l2_read_bytes; + unsigned long long l2_write_bytes; + unsigned long long l2_writes_sent; + unsigned long long l2_writes_done; + unsigned long long l2_writes_error; + unsigned long long l2_writes_lock_retry; + unsigned long long l2_evict_lock_retry; + unsigned long long l2_evict_reading; + unsigned long long l2_evict_l1cached; + unsigned long long l2_free_on_write; + unsigned long long l2_cdata_free_on_write; + unsigned long long l2_abort_lowmem; + unsigned long long l2_cksum_bad; + unsigned long long l2_io_error; + unsigned long long l2_size; + unsigned long long l2_asize; + unsigned long long l2_hdr_size; + unsigned long long l2_compress_successes; + unsigned long long l2_compress_zeros; + unsigned long long l2_compress_failures; + unsigned long long memory_throttle_count; + unsigned long long duplicate_buffers; + unsigned long long duplicate_buffers_size; + unsigned long long duplicate_reads; + unsigned long long memory_direct_count; + unsigned long long memory_indirect_count; + unsigned long long arc_no_grow; + unsigned long long arc_tempreserve; + unsigned long long arc_loaned_bytes; + unsigned long long arc_prune; + unsigned long long arc_meta_used; + unsigned long long arc_meta_limit; + unsigned long long arc_meta_max; + unsigned long long arc_meta_min; + unsigned long long arc_need_free; + unsigned long long arc_sys_free; +}; + +int l2exist; + +void generate_charts_arcstats(int update_every); +void generate_charts_arc_summary(int update_every); + +#endif //NETDATA_ZFS_COMMON_H diff --git a/system/Makefile.in b/system/Makefile.in index aa0a60e02..ff19c9463 100644 --- a/system/Makefile.in +++ b/system/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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,6 +15,51 @@ @SET_MAKE@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -34,8 +78,8 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -DIST_COMMON = $(dist_noinst_DATA) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in $(top_srcdir)/build/subst.inc +DIST_COMMON = $(top_srcdir)/build/subst.inc $(srcdir)/Makefile.in \ + $(srcdir)/Makefile.am $(dist_noinst_DATA) subdir = system ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ @@ -52,12 +96,31 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +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 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac DATA = $(dist_noinst_DATA) $(nodist_noinst_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -265,11 +328,11 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -tags: TAGS -TAGS: +tags TAGS: + +ctags CTAGS: -ctags: CTAGS -CTAGS: +cscope cscopelist: distdir: $(DISTFILES) @@ -406,15 +469,16 @@ uninstall-am: .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am 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-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am +.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ + ctags-am distclean distclean-generic distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am 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-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am tags-am uninstall uninstall-am .in: if sed \ diff --git a/web/Makefile.am b/web/Makefile.am index 03a487597..b587f5a17 100644 --- a/web/Makefile.am +++ b/web/Makefile.am @@ -17,6 +17,7 @@ dist_web_DATA = \ favicon.ico \ goto-host-from-alarm.html \ index.html \ + infographic.html \ netdata-swagger.yaml \ netdata-swagger.json \ robots.txt \ diff --git a/web/Makefile.in b/web/Makefile.in index 9ec69e6aa..42939d2a5 100644 --- a/web/Makefile.in +++ b/web/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 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,6 +15,51 @@ @SET_MAKE@ VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +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@ @@ -35,11 +79,11 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = web -DIST_COMMON = $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ $(dist_webfonts_DATA) $(dist_webimages_DATA) \ $(dist_weblib_DATA) $(dist_webold_DATA) \ - $(dist_webwellknown_DATA) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in + $(dist_webwellknown_DATA) 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_lto.m4 \ @@ -55,8 +99,25 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +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 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -92,9 +153,11 @@ DATA = $(dist_web_DATA) $(dist_webcss_DATA) $(dist_webdnt_DATA) \ $(dist_webfonts_DATA) $(dist_webimages_DATA) \ $(dist_weblib_DATA) $(dist_webold_DATA) \ $(dist_webwellknown_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -255,6 +318,7 @@ dist_web_DATA = \ favicon.ico \ goto-host-from-alarm.html \ index.html \ + infographic.html \ netdata-swagger.yaml \ netdata-swagger.json \ robots.txt \ @@ -388,8 +452,11 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-dist_webDATA: $(dist_web_DATA) @$(NORMAL_INSTALL) - test -z "$(webdir)" || $(MKDIR_P) "$(DESTDIR)$(webdir)" @list='$(dist_web_DATA)'; test -n "$(webdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -406,8 +473,11 @@ uninstall-dist_webDATA: dir='$(DESTDIR)$(webdir)'; $(am__uninstall_files_from_dir) install-dist_webcssDATA: $(dist_webcss_DATA) @$(NORMAL_INSTALL) - test -z "$(webcssdir)" || $(MKDIR_P) "$(DESTDIR)$(webcssdir)" @list='$(dist_webcss_DATA)'; test -n "$(webcssdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webcssdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webcssdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -424,8 +494,11 @@ uninstall-dist_webcssDATA: dir='$(DESTDIR)$(webcssdir)'; $(am__uninstall_files_from_dir) install-dist_webdntDATA: $(dist_webdnt_DATA) @$(NORMAL_INSTALL) - test -z "$(webdntdir)" || $(MKDIR_P) "$(DESTDIR)$(webdntdir)" @list='$(dist_webdnt_DATA)'; test -n "$(webdntdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webdntdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webdntdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -442,8 +515,11 @@ uninstall-dist_webdntDATA: dir='$(DESTDIR)$(webdntdir)'; $(am__uninstall_files_from_dir) install-dist_webfontsDATA: $(dist_webfonts_DATA) @$(NORMAL_INSTALL) - test -z "$(webfontsdir)" || $(MKDIR_P) "$(DESTDIR)$(webfontsdir)" @list='$(dist_webfonts_DATA)'; test -n "$(webfontsdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webfontsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webfontsdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -460,8 +536,11 @@ uninstall-dist_webfontsDATA: dir='$(DESTDIR)$(webfontsdir)'; $(am__uninstall_files_from_dir) install-dist_webimagesDATA: $(dist_webimages_DATA) @$(NORMAL_INSTALL) - test -z "$(webimagesdir)" || $(MKDIR_P) "$(DESTDIR)$(webimagesdir)" @list='$(dist_webimages_DATA)'; test -n "$(webimagesdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webimagesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webimagesdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -478,8 +557,11 @@ uninstall-dist_webimagesDATA: dir='$(DESTDIR)$(webimagesdir)'; $(am__uninstall_files_from_dir) install-dist_weblibDATA: $(dist_weblib_DATA) @$(NORMAL_INSTALL) - test -z "$(weblibdir)" || $(MKDIR_P) "$(DESTDIR)$(weblibdir)" @list='$(dist_weblib_DATA)'; test -n "$(weblibdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(weblibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(weblibdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -496,8 +578,11 @@ uninstall-dist_weblibDATA: dir='$(DESTDIR)$(weblibdir)'; $(am__uninstall_files_from_dir) install-dist_weboldDATA: $(dist_webold_DATA) @$(NORMAL_INSTALL) - test -z "$(webolddir)" || $(MKDIR_P) "$(DESTDIR)$(webolddir)" @list='$(dist_webold_DATA)'; test -n "$(webolddir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webolddir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webolddir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -514,8 +599,11 @@ uninstall-dist_weboldDATA: dir='$(DESTDIR)$(webolddir)'; $(am__uninstall_files_from_dir) install-dist_webwellknownDATA: $(dist_webwellknown_DATA) @$(NORMAL_INSTALL) - test -z "$(webwellknowndir)" || $(MKDIR_P) "$(DESTDIR)$(webwellknowndir)" @list='$(dist_webwellknown_DATA)'; test -n "$(webwellknowndir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(webwellknowndir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(webwellknowndir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -530,11 +618,11 @@ uninstall-dist_webwellknownDATA: @list='$(dist_webwellknown_DATA)'; test -n "$(webwellknowndir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(webwellknowndir)'; $(am__uninstall_files_from_dir) -tags: TAGS -TAGS: +tags TAGS: + +ctags CTAGS: -ctags: CTAGS -CTAGS: +cscope cscopelist: distdir: $(DISTFILES) @@ -679,10 +767,10 @@ uninstall-am: uninstall-dist_webDATA uninstall-dist_webcssDATA \ .MAKE: install-am install-strip -.PHONY: all all-am check check-am clean clean-generic distclean \ - distclean-generic distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-dist_webDATA install-dist_webcssDATA \ +.PHONY: all all-am check check-am clean clean-generic cscopelist-am \ + ctags-am distclean distclean-generic distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dist_webDATA install-dist_webcssDATA \ install-dist_webdntDATA install-dist_webfontsDATA \ install-dist_webimagesDATA install-dist_weblibDATA \ install-dist_weboldDATA install-dist_webwellknownDATA \ @@ -691,11 +779,12 @@ uninstall-am: uninstall-dist_webDATA uninstall-dist_webcssDATA \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ - uninstall-am uninstall-dist_webDATA uninstall-dist_webcssDATA \ - uninstall-dist_webdntDATA uninstall-dist_webfontsDATA \ - uninstall-dist_webimagesDATA uninstall-dist_weblibDATA \ - uninstall-dist_weboldDATA uninstall-dist_webwellknownDATA + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags-am \ + uninstall uninstall-am uninstall-dist_webDATA \ + uninstall-dist_webcssDATA uninstall-dist_webdntDATA \ + uninstall-dist_webfontsDATA uninstall-dist_webimagesDATA \ + uninstall-dist_weblibDATA uninstall-dist_weboldDATA \ + uninstall-dist_webwellknownDATA version.txt: diff --git a/web/dashboard.css b/web/dashboard.css index 8eeaa8bec..2147c6038 100644 --- a/web/dashboard.css +++ b/web/dashboard.css @@ -49,6 +49,38 @@ body { /* width and height is given per chart with data-width and data-height */ } +.netdata-container-gauge { + display: inline-block; + overflow: hidden; + + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ +} + +.netdata-container-gauge:after { + padding-top: 60%; + display: block; + content: ''; +} + +.netdata-container-easypiechart { + display: inline-block; + overflow: hidden; + + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ +} + +.netdata-container-easypiechart:after { + padding-top: 100%; + display: block; + content: ''; +} + .netdata-aspect { position: relative; width: 100%; @@ -128,12 +160,15 @@ body { .netdata-message { display: inline-block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; text-align: left; vertical-align: top; font-weight: bold; font-size: x-small; - width: 100%; - height: 100%; overflow: hidden; background: inherit; z-index: 0; @@ -399,7 +434,7 @@ body { margin-left: 18%; text-align: center; color: #999999; - font-weight: normal; + font-weight: bold; } .easyPieChartUnits { @@ -423,6 +458,8 @@ body { position: absolute; top: 0; left: 0; + bottom: 0; + right: 0; z-index: 0; } @@ -471,7 +508,7 @@ body { position: absolute; float: left; left: 0; - bottom: 10%; + bottom: 8%; width: 92%; margin-left: 8%; text-align: left; @@ -484,7 +521,7 @@ body { position: absolute; float: left; left: 0; - bottom: 10%; + bottom: 8%; width: 95%; margin-right: 5%; text-align: right; diff --git a/web/dashboard.html b/web/dashboard.html index 75de65ad9..4453c996e 100644 --- a/web/dashboard.html +++ b/web/dashboard.html @@ -652,4 +652,4 @@ So, to avoid flashing the charts, we destroy and re-create the charts on each up - + diff --git a/web/dashboard.js b/web/dashboard.js index b34accdec..1f240a4c8 100644 --- a/web/dashboard.js +++ b/web/dashboard.js @@ -144,7 +144,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.themes = { white: { bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-3.3.7.css', - dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20161229-2', + dashboard_css: NETDATA.serverDefault + 'dashboard.css?v20170605-2', background: '#FFFFFF', foreground: '#000000', grid: '#F0F0F0', @@ -161,7 +161,7 @@ var NETDATA = window.NETDATA || {}; }, slate: { bootstrap_css: NETDATA.serverDefault + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1', - dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20161229-2', + dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css?v20170605-2', background: '#272b30', foreground: '#C8C8C8', grid: '#283236', @@ -287,7 +287,7 @@ var NETDATA = window.NETDATA || {}; // rendering the chart that is panned or zoomed). // Used with .current.global_pan_sync_time - last_resized: Date.now(), // the timestamp of the last resize request + last_page_resize: Date.now(), // the timestamp of the last resize request last_page_scroll: 0, // the timestamp the last time the page was scrolled @@ -571,7 +571,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.onresizeCallback = null; NETDATA.onresize = function() { - NETDATA.options.last_resized = Date.now(); + NETDATA.options.last_page_resize = Date.now(); NETDATA.onscroll(); if(typeof NETDATA.onresizeCallback === 'function') @@ -602,18 +602,18 @@ var NETDATA = window.NETDATA || {}; // we have to cancel pending requests too while (len--) { - if (targets[len]._updating === true) { + if (targets[len].fetching_data === true) { if (typeof targets[len].xhr !== 'undefined') { targets[len].xhr.abort(); targets[len].running = false; - targets[len]._updating = false; + targets[len].fetching_data = false; } targets[len].isVisible(); } } } else { - // just find which chart is visible + // just find which charts are visible while (len--) targets[len].isVisible(); @@ -644,9 +644,14 @@ var NETDATA = window.NETDATA || {}; NETDATA.onscroll_updater_running = false; }; + NETDATA.scrollUp = false; + NETDATA.scrollY = window.scrollY; NETDATA.onscroll = function() { // console.log('onscroll'); + NETDATA.scrollUp = (window.scrollY > NETDATA.scrollY); + NETDATA.scrollY = window.scrollY; + NETDATA.options.last_page_scroll = Date.now(); NETDATA.options.auto_refresher_stop_until = 0; @@ -733,6 +738,254 @@ var NETDATA = window.NETDATA || {}; NETDATA.errorLast.datetime = 0; }; + // ---------------------------------------------------------------------------------------------------------------- + // fast numbers formatting + + NETDATA.fastNumberFormat = { + formatters_fixed: [], + formatters_zero_based: [], + + // this is the fastest and the preferred + getIntlNumberFormat: function(min, max) { + var key = max; + if(min === max) { + if(typeof this.formatters_fixed[key] === 'undefined') + this.formatters_fixed[key] = new Intl.NumberFormat(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + + return this.formatters_fixed[key]; + } + else if(min === 0) { + if(typeof this.formatters_zero_based[key] === 'undefined') + this.formatters_zero_based[key] = new Intl.NumberFormat(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + + return this.formatters_zero_based[key]; + } + else { + // this is never used + // it is added just for completeness + return new Intl.NumberFormat(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + } + }, + + // this respects locale + getLocaleString: function(min, max) { + var key = max; + if(min === max) { + if(typeof this.formatters_fixed[key] === 'undefined') + this.formatters_fixed[key] = { + format: function (value) { + return value.toLocaleString(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + } + }; + + return this.formatters_fixed[key]; + } + else if(min === 0) { + if(typeof this.formatters_zero_based[key] === 'undefined') + this.formatters_zero_based[key] = { + format: function (value) { + return value.toLocaleString(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + } + }; + + return this.formatters_zero_based[key]; + } + else { + return { + format: function (value) { + return value.toLocaleString(undefined, { + // style: 'decimal', + // minimumIntegerDigits: 1, + // minimumSignificantDigits: 1, + // maximumSignificantDigits: 1, + useGrouping: true, + minimumFractionDigits: min, + maximumFractionDigits: max + }); + } + }; + } + }, + + getFixed: function(min, max) { + var key = max; + if(min === max) { + if(typeof this.formatters_fixed[key] === 'undefined') + this.formatters_fixed[key] = { + format: function (value) { + if(value === 0) return "0"; + return value.toFixed(max); + } + }; + + return this.formatters_fixed[key]; + } + else if(min === 0) { + if(typeof this.formatters_zero_based[key] === 'undefined') + this.formatters_zero_based[key] = { + format: function (value) { + if(value === 0) return "0"; + return value.toFixed(max); + } + }; + + return this.formatters_zero_based[key]; + } + else { + return { + format: function (value) { + if(value === 0) return "0"; + return value.toFixed(max); + } + }; + } + }, + + testIntlNumberFormat: function() { + var n = 1.12345; + var e1 = "1.12", e2 = "1,12"; + var s = ""; + + try { + var x = new Intl.NumberFormat(undefined, { + useGrouping: true, + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }); + + s = x.format(n); + } + catch(e) { + s = ""; + } + + // console.log('NumberFormat: ', s); + return (s === e1 || s === e2); + }, + + testLocaleString: function() { + var n = 1.12345; + var e1 = "1.12", e2 = "1,12"; + var s = ""; + + try { + s = value.toLocaleString(undefined, { + useGrouping: true, + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }); + } + catch(e) { + s = ""; + } + + // console.log('localeString: ', s); + return (s === e1 || s === e2); + }, + + // on first run we decide which formatter to use + get: function(min, max) { + if(this.testIntlNumberFormat()) { + // console.log('numberformat'); + this.get = this.getIntlNumberFormat; + } + else if(this.testLocaleString()) { + // console.log('localestring'); + this.get = this.getLocaleString; + } + else { + // console.log('fixed'); + this.get = this.getFixed; + } + return this.get(min, max); + } + }; + + // ---------------------------------------------------------------------------------------------------------------- + // element data attributes + + NETDATA.dataAttribute = function(element, attribute, def) { + var key = 'data-' + attribute.toString(); + if(element.hasAttribute(key) === true) { + var data = element.getAttribute(key); + + if(data === 'true') return true; + if(data === 'false') return false; + if(data === 'null') return null; + + // Only convert to a number if it doesn't change the string + if(data === +data + '') return +data; + + if(/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/.test(data)) + return JSON.parse(data); + + return data; + } + else return def; + }; + + NETDATA.dataAttributeBoolean = function(element, attribute, def) { + var value = NETDATA.dataAttribute(element, attribute, def); + + if(value === true || value === false) + return value; + + if(typeof(value) === 'string') { + if(value === 'yes' || value === 'on') + return true; + + if(value === '' || value === 'no' || value === 'off' || value === 'null') + return false; + + return def; + } + + if(typeof(value) === 'number') + return value !== 0; + + return def; + }; + // ---------------------------------------------------------------------------------------------------------------- // commonMin & commonMax @@ -741,14 +994,13 @@ var NETDATA = window.NETDATA || {}; latest: {}, get: function(state) { - if(typeof state.__commonMin === 'undefined') { + if(typeof state.tmp.__commonMin === 'undefined') { // get the commonMin setting - var self = $(state.element); - state.__commonMin = self.data('common-min') || null; + state.tmp.__commonMin = NETDATA.dataAttribute(state.element, 'common-min', null); } var min = state.data.min; - var name = state.__commonMin; + var name = state.tmp.__commonMin; if(name === null) { // we don't need commonMin @@ -766,11 +1018,11 @@ var NETDATA = window.NETDATA || {}; var uuid = state.uuid; if(typeof t[uuid] !== 'undefined') { if(t[uuid] === min) { - //state.log('commonMin ' + state.__commonMin + ' not changed: ' + this.latest[name]); + //state.log('commonMin ' + state.tmp.__commonMin + ' not changed: ' + this.latest[name]); return this.latest[name]; } else if(min < this.latest[name]) { - //state.log('commonMin ' + state.__commonMin + ' increased: ' + min); + //state.log('commonMin ' + state.tmp.__commonMin + ' increased: ' + min); t[uuid] = min; this.latest[name] = min; return min; @@ -785,7 +1037,7 @@ var NETDATA = window.NETDATA || {}; for(var i in t) if(t.hasOwnProperty(i) && t[i] < m) m = t[i]; - //state.log('commonMin ' + state.__commonMin + ' updated: ' + m); + //state.log('commonMin ' + state.tmp.__commonMin + ' updated: ' + m); this.latest[name] = m; return m; } @@ -796,14 +1048,13 @@ var NETDATA = window.NETDATA || {}; latest: {}, get: function(state) { - if(typeof state.__commonMax === 'undefined') { + if(typeof state.tmp.__commonMax === 'undefined') { // get the commonMax setting - var self = $(state.element); - state.__commonMax = self.data('common-max') || null; + state.tmp.__commonMax = NETDATA.dataAttribute(state.element, 'common-max', null); } var max = state.data.max; - var name = state.__commonMax; + var name = state.tmp.__commonMax; if(name === null) { // we don't need commonMax @@ -821,11 +1072,11 @@ var NETDATA = window.NETDATA || {}; var uuid = state.uuid; if(typeof t[uuid] !== 'undefined') { if(t[uuid] === max) { - //state.log('commonMax ' + state.__commonMax + ' not changed: ' + this.latest[name]); + //state.log('commonMax ' + state.tmp.__commonMax + ' not changed: ' + this.latest[name]); return this.latest[name]; } else if(max > this.latest[name]) { - //state.log('commonMax ' + state.__commonMax + ' increased: ' + max); + //state.log('commonMax ' + state.tmp.__commonMax + ' increased: ' + max); t[uuid] = max; this.latest[name] = max; return max; @@ -840,7 +1091,7 @@ var NETDATA = window.NETDATA || {}; for(var i in t) if(t.hasOwnProperty(i) && t[i] > m) m = t[i]; - //state.log('commonMax ' + state.__commonMax + ' updated: ' + m); + //state.log('commonMax ' + state.tmp.__commonMax + ' updated: ' + m); this.latest[name] = m; return m; } @@ -1237,13 +1488,15 @@ var NETDATA = window.NETDATA || {}; // Our state object, where all per-chart values are stored var chartState = function(element) { - var self = $(element); this.element = element; // IMPORTANT: // all private functions should use 'that', instead of 'this' var that = this; + // ============================================================================================================ + // ERROR HANDLING + /* error() - private * show an error instead of the chart */ @@ -1261,18 +1514,34 @@ var NETDATA = window.NETDATA || {}; } }; + // console logging + this.log = function(msg) { + console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg); + }; + + + // ============================================================================================================ + // EARLY INITIALIZATION + + // These are variables that should exist even if the chart is never to be rendered. + // Be careful what you add here - there may be thousands of charts on the page. + // GUID - a unique identifier for the chart this.uuid = NETDATA.guid(); // string - the name of chart - this.id = self.data('netdata'); + this.id = NETDATA.dataAttribute(this.element, 'netdata', undefined); + if(typeof this.id === 'undefined') { + error("netdata elements need data-netdata"); + return; + } // string - the key for localStorage settings - this.settings_id = self.data('id') || null; + this.settings_id = NETDATA.dataAttribute(this.element, 'id', null); // the user given dimensions of the element - this.width = self.data('width') || NETDATA.chartDefaults.width; - this.height = self.data('height') || NETDATA.chartDefaults.height; + this.width = NETDATA.dataAttribute(this.element, 'width', NETDATA.chartDefaults.width); + this.height = NETDATA.dataAttribute(this.element, 'height', NETDATA.chartDefaults.height); this.height_original = this.height; if(this.settings_id !== null) { @@ -1285,86 +1554,24 @@ var NETDATA = window.NETDATA || {}; }); } - // string - the netdata server URL, without any path - this.host = self.data('host') || NETDATA.chartDefaults.host; - - // make sure the host does not end with / - // all netdata API requests use absolute paths - while(this.host.slice(-1) === '/') - this.host = this.host.substring(0, this.host.length - 1); - - // string - the grouping method requested by the user - this.method = self.data('method') || NETDATA.chartDefaults.method; - - // the time-range requested by the user - this.after = self.data('after') || NETDATA.chartDefaults.after; - this.before = self.data('before') || NETDATA.chartDefaults.before; - - // the pixels per point requested by the user - this.pixels_per_point = self.data('pixels-per-point') || 1; - this.points = self.data('points') || null; - - // the dimensions requested by the user - this.dimensions = self.data('dimensions') || null; - // the chart library requested by the user - this.library_name = self.data('chart-library') || NETDATA.chartDefaults.library; - - // how many retries we have made to load chart data from the server - this.retries_on_data_failures = 0; - - // object - the chart library used - this.library = null; - - // color management - this.colors = null; - this.colors_assigned = {}; - this.colors_available = null; - - // the element already created by the user - this.element_message = null; - - // the element with the chart - this.element_chart = null; - - // the element with the legend of the chart (if created by us) - this.element_legend = null; - this.element_legend_childs = { - hidden: null, - title_date: null, - title_time: null, - title_units: null, - perfect_scroller: null, // the container to apply perfect scroller to - series: null - }; - - this.chart_url = null; // string - the url to download chart info - this.chart = null; // object - the chart as downloaded from the server - - this.title = self.data('title') || null; // the title of the chart - this.units = self.data('units') || null; // the units of the chart dimensions - this.append_options = self.data('append-options') || null; // additional options to pass to netdata - this.override_options = self.data('override-options') || null; // override options to pass to netdata - - this.running = false; // boolean - true when the chart is being refreshed now - this.enabled = true; // boolean - is the chart enabled for refresh? - this.paused = false; // boolean - is the chart paused for any reason? - this.selected = false; // boolean - is the chart shown a selection? - this.debug = false; // boolean - console.log() debug info about this chart - - this.netdata_first = 0; // milliseconds - the first timestamp in netdata - this.netdata_last = 0; // milliseconds - the last timestamp in netdata - this.requested_after = null; // milliseconds - the timestamp of the request after param - this.requested_before = null; // milliseconds - the timestamp of the request before param - this.requested_padding = null; - this.view_after = 0; - this.view_before = 0; + this.library_name = NETDATA.dataAttribute(this.element, 'chart-library', NETDATA.chartDefaults.library); - this.value_decimal_detail = -1; - var d = self.data('decimal-digits'); - if(typeof d === 'number') { - this.value_decimal_detail = d; + // check the requested library is available + // we don't initialize it here - it will be initialized when + // this chart will be first used + if(typeof NETDATA.chartLibraries[this.library_name] === 'undefined') { + NETDATA.error(402, this.library_name); + error('chart library "' + this.library_name + '" is not found'); + this.enabled = false; + } + else if(NETDATA.chartLibraries[this.library_name].enabled === false) { + NETDATA.error(403, this.library_name); + error('chart library "' + this.library_name + '" is not enabled'); + this.enabled = false; } + else + this.library = NETDATA.chartLibraries[this.library_name]; this.auto = { name: 'auto', @@ -1392,79 +1599,192 @@ var NETDATA = window.NETDATA || {}; // auto, pan, zoom this.current = this.auto; - // check the requested library is available - // we don't initialize it here - it will be initialized when - // this chart will be first used - if(typeof NETDATA.chartLibraries[that.library_name] === 'undefined') { - NETDATA.error(402, that.library_name); - error('chart library "' + that.library_name + '" is not found'); - return; - } - else if(NETDATA.chartLibraries[that.library_name].enabled === false) { - NETDATA.error(403, that.library_name); - error('chart library "' + that.library_name + '" is not enabled'); - return; - } - else - that.library = NETDATA.chartLibraries[that.library_name]; + this.running = false; // boolean - true when the chart is being refreshed now + this.enabled = true; // boolean - is the chart enabled for refresh? - // milliseconds - the time the last refresh took - this.refresh_dt_ms = 0; + that.tmp = {}; - // if we need to report the rendering speed - // find the element that needs to be updated - var refresh_dt_element_name = self.data('dt-element-name') || null; // string - the element to print refresh_dt_ms + // ============================================================================================================ + // PRIVATE FUNCTIONS - if(refresh_dt_element_name !== null) { - this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null; - } - else - this.refresh_dt_element = null; + // reset the runtime status variables to their defaults + var runtimeInit = function() { + that.paused = false; // boolean - is the chart paused for any reason? + that.selected = false; // boolean - is the chart shown a selection? - this.dimensions_visibility = new dimensionsVisibility(this); + that.chart_created = false; // boolean - is the library.create() been called? + that.dom_created = false; // boolean - is the chart DOM been created? + that.fetching_data = false; // boolean - true while we fetch data via ajax - this._updating = false; + that.updates_counter = 0; // numeric - the number of refreshes made so far + that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden + that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created - // ============================================================================================================ - // PRIVATE FUNCTIONS + that.tm = { + last_initialized: 0, // milliseconds - the timestamp it was last initialized + last_dom_created: 0, // milliseconds - the timestamp its DOM was last created + last_mode_switch: 0, // milliseconds - the timestamp it switched modes + + last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart + last_updated: 0, // the timestamp the chart last updated with data + pan_and_zoom_seq: 0, // the sequence number of the global synchronization + // between chart. + // Used with NETDATA.globalPanAndZoom.seq + last_visible_check: 0, // the time we last checked if it is visible + last_resized: 0, // the time the chart was resized + last_hidden: 0, // the time the chart was hidden + last_unhidden: 0, // the time the chart was unhidden + last_autorefreshed: 0 // the time the chart was last refreshed + }; - var createDOM = function() { + that.data = null; // the last data as downloaded from the netdata server + that.data_url = 'invalid://'; // string - the last url used to update the chart + that.data_points = 0; // number - the number of points returned from netdata + that.data_after = 0; // milliseconds - the first timestamp of the data + that.data_before = 0; // milliseconds - the last timestamp of the data + that.data_update_every = 0; // milliseconds - the frequency to update the data + + that.tmp = {}; // members that can be destroyed to save memory + }; + + // initialize all the variables that are required for the chart to be rendered + var lateInitialization = function() { + if(typeof that.host !== 'undefined') + return; + + // string - the netdata server URL, without any path + that.host = NETDATA.dataAttribute(that.element, 'host', NETDATA.chartDefaults.host); + + // make sure the host does not end with / + // all netdata API requests use absolute paths + while(that.host.slice(-1) === '/') + that.host = that.host.substring(0, that.host.length - 1); + + // string - the grouping method requested by the user + that.method = NETDATA.dataAttribute(that.element, 'method', NETDATA.chartDefaults.method); + + // the time-range requested by the user + that.after = NETDATA.dataAttribute(that.element, 'after', NETDATA.chartDefaults.after); + that.before = NETDATA.dataAttribute(that.element, 'before', NETDATA.chartDefaults.before); + + // the pixels per point requested by the user + that.pixels_per_point = NETDATA.dataAttribute(that.element, 'pixels-per-point', 1); + that.points = NETDATA.dataAttribute(that.element, 'points', null); + + // the dimensions requested by the user + that.dimensions = NETDATA.dataAttribute(that.element, 'dimensions', null); + + that.title = NETDATA.dataAttribute(that.element, 'title', null); // the title of the chart + that.units = NETDATA.dataAttribute(that.element, 'units', null); // the units of the chart dimensions + that.append_options = NETDATA.dataAttribute(that.element, 'append-options', null); // additional options to pass to netdata + that.override_options = NETDATA.dataAttribute(that.element, 'override-options', null); // override options to pass to netdata + + that.debug = NETDATA.dataAttributeBoolean(that.element, 'debug', false); + + that.value_decimal_detail = -1; + var d = NETDATA.dataAttribute(that.element, 'decimal-digits', -1); + if(typeof d === 'number') + that.value_decimal_detail = d; + else if(typeof d !== 'undefined') + that.log('ignoring decimal-digits value: ' + d.toString()); + + // if we need to report the rendering speed + // find the element that needs to be updated + var refresh_dt_element_name = NETDATA.dataAttribute(that.element, 'dt-element-name', null); // string - the element to print refresh_dt_ms + + if(refresh_dt_element_name !== null) { + that.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null; + } + else + that.refresh_dt_element = null; + + that.dimensions_visibility = new dimensionsVisibility(that); + + that.netdata_first = 0; // milliseconds - the first timestamp in netdata + that.netdata_last = 0; // milliseconds - the last timestamp in netdata + that.requested_after = null; // milliseconds - the timestamp of the request after param + that.requested_before = null; // milliseconds - the timestamp of the request before param + that.requested_padding = null; + that.view_after = 0; + that.view_before = 0; + + that.refresh_dt_ms = 0; // milliseconds - the time the last refresh took + + // how many retries we have made to load chart data from the server + that.retries_on_data_failures = 0; + + // color management + that.colors = null; + that.colors_assigned = {}; + that.colors_available = null; + that.colors_custom = null; + + that.element_message = null; // the element already created by the user + that.element_chart = null; // the element with the chart + that.element_legend = null; // the element with the legend of the chart (if created by us) + that.element_legend_childs = { + hidden: null, + title_date: null, + title_time: null, + title_units: null, + perfect_scroller: null, // the container to apply perfect scroller to + series: null + }; + + that.chart_url = null; // string - the url to download chart info + that.chart = null; // object - the chart as downloaded from the server + }; + + var destroyDOM = function() { if(that.enabled === false) return; - if(that.element_message !== null) that.element_message.innerHTML = ''; - if(that.element_legend !== null) that.element_legend.innerHTML = ''; - if(that.element_chart !== null) that.element_chart.innerHTML = ''; + if(that.debug === true) + that.log('destroyDOM()'); + // that.element.className = 'netdata-message icon'; + // that.element.innerHTML = ' netdata'; that.element.innerHTML = ''; + that.element_message = null; + that.element_legend = null; + that.element_chart = null; + that.element_legend_childs.series = null; + + that.chart_created = false; + that.dom_created = false; + + that.tm.last_resized = 0; + that.tm.last_dom_created = 0; + }; + + var createDOM = function() { + if(that.enabled === false) return; + lateInitialization(); + + destroyDOM(); + + if(that.debug === true) + that.log('createDOM()'); that.element_message = document.createElement('div'); that.element_message.className = 'netdata-message icon hidden'; that.element.appendChild(that.element_message); - that.element_chart = document.createElement('div'); - that.element_chart.id = that.library_name + '-' + that.uuid + '-chart'; - that.element.appendChild(that.element_chart); + that.dom_created = true; + that.chart_created = false; - if(that.hasLegend() === true) { - that.element.className = "netdata-container-with-legend"; - that.element_chart.className = 'netdata-chart-with-legend-right netdata-' + that.library_name + '-chart-with-legend-right'; + that.tm.last_dom_created = + that.tm.last_resized = Date.now(); - that.element_legend = document.createElement('div'); - that.element_legend.className = 'netdata-chart-legend netdata-' + that.library_name + '-legend'; - that.element.appendChild(that.element_legend); - } - else { - that.element.className = "netdata-container"; - that.element_chart.className = ' netdata-chart netdata-' + that.library_name + '-chart'; + showLoading(); + }; - that.element_legend = null; - } - that.element_legend_childs.series = null; + var initDOM = function() { + that.element.className = that.library.container_class(that); if(typeof(that.width) === 'string') - $(that.element).css('width', that.width); + that.element.style.width = that.width; else if(typeof(that.width) === 'number') - $(that.element).css('width', that.width + 'px'); + that.element.style.width = that.width.toString() + 'px'; if(typeof(that.library.aspect_ratio) === 'undefined') { if(typeof(that.height) === 'string') @@ -1472,23 +1792,9 @@ var NETDATA = window.NETDATA || {}; else if(typeof(that.height) === 'number') that.element.style.height = that.height.toString() + 'px'; } - else { - var w = that.element.offsetWidth; - if(w === null || w === 0) { - // the div is hidden - // this will resize the chart when next viewed - that.tm.last_resized = 0; - } - else - that.element.style.height = (w * that.library.aspect_ratio / 100).toString() + 'px'; - } if(NETDATA.chartDefaults.min_width !== null) - $(that.element).css('min-width', NETDATA.chartDefaults.min_width); - - that.tm.last_dom_created = Date.now(); - - showLoading(); + that.element.style.min_width = NETDATA.chartDefaults.min_width; }; /* init() private @@ -1496,45 +1802,18 @@ var NETDATA = window.NETDATA || {}; * destroy all (possibly) created state elements * create the basic DOM for a chart */ - var init = function() { + var init = function(opt) { if(that.enabled === false) return; - that.paused = false; - that.selected = false; - - that.chart_created = false; // boolean - is the library.create() been called? - that.updates_counter = 0; // numeric - the number of refreshes made so far - that.updates_since_last_unhide = 0; // numeric - the number of refreshes made since the last time the chart was unhidden - that.updates_since_last_creation = 0; // numeric - the number of refreshes made since the last time the chart was created - - that.tm = { - last_initialized: 0, // milliseconds - the timestamp it was last initialized - last_dom_created: 0, // milliseconds - the timestamp its DOM was last created - last_mode_switch: 0, // milliseconds - the timestamp it switched modes - - last_info_downloaded: 0, // milliseconds - the timestamp we downloaded the chart - last_updated: 0, // the timestamp the chart last updated with data - pan_and_zoom_seq: 0, // the sequence number of the global synchronization - // between chart. - // Used with NETDATA.globalPanAndZoom.seq - last_visible_check: 0, // the time we last checked if it is visible - last_resized: 0, // the time the chart was resized - last_hidden: 0, // the time the chart was hidden - last_unhidden: 0, // the time the chart was unhidden - last_autorefreshed: 0 // the time the chart was last refreshed - }; - - that.data = null; // the last data as downloaded from the netdata server - that.data_url = 'invalid://'; // string - the last url used to update the chart - that.data_points = 0; // number - the number of points returned from netdata - that.data_after = 0; // milliseconds - the first timestamp of the data - that.data_before = 0; // milliseconds - the last timestamp of the data - that.data_update_every = 0; // milliseconds - the frequency to update the data + runtimeInit(); that.tm.last_initialized = Date.now(); - createDOM(); - that.setMode('auto'); + + if(opt !== 'fast') { + if (that.isVisible(true) || opt === 'force') + createDOM(); + } }; var maxMessageFontSize = function() { @@ -1575,12 +1854,12 @@ var NETDATA = window.NETDATA || {}; that.element_message.innerHTML = icon; maxMessageFontSize(); $(that.element_message).removeClass('hidden'); - that.___messageHidden___ = undefined; + that.tmp.___messageHidden___ = undefined; }; var hideMessage = function() { - if(typeof that.___messageHidden___ === 'undefined') { - that.___messageHidden___ = true; + if(typeof that.tmp.___messageHidden___ === 'undefined') { + that.tmp.___messageHidden___ = true; $(that.element_message).addClass('hidden'); } }; @@ -1608,7 +1887,7 @@ var NETDATA = window.NETDATA || {}; }; var isHidden = function() { - return (typeof that.___chartIsHidden___ !== 'undefined'); + return (typeof that.tmp.___chartIsHidden___ !== 'undefined'); }; // hide the chart, when it is not visible - called from isVisible() @@ -1619,12 +1898,15 @@ var NETDATA = window.NETDATA || {}; if(that.chart_created === true) { if(NETDATA.options.current.destroy_on_hide === true) { // we should destroy it - init(); + init('force'); } else { showRendering(); that.element_chart.style.display = 'none'; if(that.element_legend !== null) that.element_legend.style.display = 'none'; + if(that.element_legend_childs.toolbox !== null) that.element_legend_childs.toolbox.style.display = 'none'; + if(that.element_legend_childs.resize_handler !== null) that.element_legend_childs.resize_handler.style.display = 'none'; + that.tm.last_hidden = Date.now(); // de-allocate data @@ -1635,25 +1917,27 @@ var NETDATA = window.NETDATA || {}; } } - that.___chartIsHidden___ = true; + that.tmp.___chartIsHidden___ = true; }; // unhide the chart, when it is visible - called from isVisible() var unhideChart = function() { if(isHidden() === false) return; - that.___chartIsHidden___ = undefined; + that.tmp.___chartIsHidden___ = undefined; that.updates_since_last_unhide = 0; if(that.chart_created === false) { // we need to re-initialize it, to show our background // logo in bootstrap tabs, until the chart loads - init(); + init('force'); } else { that.tm.last_unhidden = Date.now(); that.element_chart.style.display = ''; if(that.element_legend !== null) that.element_legend.style.display = ''; + if(that.element_legend_childs.toolbox !== null) that.element_legend_childs.toolbox.style.display = ''; + if(that.element_legend_childs.resize_handler !== null) that.element_legend_childs.resize_handler.style.display = ''; resizeChart(); hideMessage(); } @@ -1724,11 +2008,11 @@ var NETDATA = window.NETDATA || {}; // to be called just before the chart library to make sure that // a properly sized dom is available var resizeChart = function() { - if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_resized) { + if(that.isVisible() === true && that.tm.last_resized < NETDATA.options.last_page_resize) { if(that.chart_created === false) return; if(that.needsRecreation()) { - init(); + init('force'); } else if(typeof that.library.resize === 'function') { that.library.resize(that); @@ -1900,7 +2184,7 @@ var NETDATA = window.NETDATA || {}; // that.data_update_every = 30 * 1000; //that.element_chart.style.display = 'none'; //if(that.element_legend !== null) that.element_legend.style.display = 'none'; - //that.___chartIsHidden___ = true; + //that.tmp.___chartIsHidden___ = true; }; // ============================================================================================================ @@ -2104,11 +2388,6 @@ var NETDATA = window.NETDATA || {}; // ---------------------------------------------------------------------------------------------------------------- - // console logging - this.log = function(msg) { - console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg); - }; - this.pauseChart = function() { if(this.paused === false) { if(this.debug === true) @@ -2248,6 +2527,7 @@ var NETDATA = window.NETDATA || {}; var __legendFormatValueChartDecimalsLastMin = undefined; var __legendFormatValueChartDecimalsLastMax = undefined; var __legendFormatValueChartDecimals = -1; + var __intlNumberFormat = null; this.legendFormatValueDecimalsFromMinMax = function(min, max) { if(min === __legendFormatValueChartDecimalsLastMin && max === __legendFormatValueChartDecimalsLastMax) return; @@ -2255,13 +2535,18 @@ var NETDATA = window.NETDATA || {}; __legendFormatValueChartDecimalsLastMin = min; __legendFormatValueChartDecimalsLastMax = max; + var old = __legendFormatValueChartDecimals; + if(this.data !== null && this.data.min === this.data.max) + // it is a fixed number, let the visualizer decide based on the value __legendFormatValueChartDecimals = -1; else if(this.value_decimal_detail !== -1) + // there is an override __legendFormatValueChartDecimals = this.value_decimal_detail; else { + // ok, let's calculate the proper number of decimal points var delta; if (min === max) @@ -2275,39 +2560,39 @@ var NETDATA = window.NETDATA || {}; else if (delta > 0.1) __legendFormatValueChartDecimals = 2; else __legendFormatValueChartDecimals = 4; } + + if(__legendFormatValueChartDecimals !== old) { + if(__legendFormatValueChartDecimals < 0) + __intlNumberFormat = null; + else + __intlNumberFormat = NETDATA.fastNumberFormat.get( + __legendFormatValueChartDecimals, + __legendFormatValueChartDecimals + ); + } }; this.legendFormatValue = function(value) { if(typeof value !== 'number') return '-'; - var dmin, dmax; - - if(__legendFormatValueChartDecimals < 0) { - dmin = 0; - var abs = value; - if(abs > 1000) dmax = 0; - else if(abs > 10 ) dmax = 1; - else if(abs > 1) dmax = 2; - else if(abs > 0.1) dmax = 2; - else dmax = 4; - } - else { - dmin = dmax = __legendFormatValueChartDecimals; - } + if(__intlNumberFormat !== null) + return __intlNumberFormat.format(value); + var dmin, dmax; if(this.value_decimal_detail !== -1) { dmin = dmax = this.value_decimal_detail; } + else { + dmin = 0; + var abs = (value < 0) ? -value : value; + if (abs > 1000) dmax = 0; + else if (abs > 10) dmax = 1; + else if (abs > 1) dmax = 2; + else if (abs > 0.1) dmax = 2; + else dmax = 4; + } - return value.toLocaleString(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: dmin, - maximumFractionDigits: dmax - }); + return NETDATA.fastNumberFormat.get(dmin, dmax).format(value); }; this.legendSetLabelValue = function(label, value) { @@ -2357,23 +2642,23 @@ var NETDATA = window.NETDATA || {}; }; this.__legendSetDateString = function(date) { - if(date !== this.__last_shown_legend_date) { + if(date !== this.tmp.__last_shown_legend_date) { this.element_legend_childs.title_date.innerText = date; - this.__last_shown_legend_date = date; + this.tmp.__last_shown_legend_date = date; } }; this.__legendSetTimeString = function(time) { - if(time !== this.__last_shown_legend_time) { + if(time !== this.tmp.__last_shown_legend_time) { this.element_legend_childs.title_time.innerText = time; - this.__last_shown_legend_time = time; + this.tmp.__last_shown_legend_time = time; } }; this.__legendSetUnitsString = function(units) { - if(units !== this.__last_shown_legend_units) { + if(units !== this.tmp.__last_shown_legend_units) { this.element_legend_childs.title_units.innerText = units; - this.__last_shown_legend_units = units; + this.tmp.__last_shown_legend_units = units; } }; @@ -2468,16 +2753,28 @@ var NETDATA = window.NETDATA || {}; }; // this should be called just ONCE per dimension per chart - this._chartDimensionColor = function(label) { - if(this.colors === null) this.chartColors(); + this.__chartDimensionColor = function(label) { + this.chartPrepareColorPalette(); if(typeof this.colors_assigned[label] === 'undefined') { + if(this.colors_available.length === 0) { - var len = NETDATA.themes.current.colors.length; + var len; + + // copy the custom colors + if(this.colors_custom !== null) { + len = this.colors_custom.length; + while (len--) + this.colors_available.unshift(this.colors_custom[len]); + } + + // copy the standard palette colors + len = NETDATA.themes.current.colors.length; while(len--) this.colors_available.unshift(NETDATA.themes.current.colors[len]); } + // assign a color to this dimension this.colors_assigned[label] = this.colors_available.shift(); if(this.debug === true) @@ -2492,39 +2789,77 @@ var NETDATA = window.NETDATA || {}; return this.colors_assigned[label]; }; - this.chartColors = function() { - if(this.colors !== null) return this.colors; + this.chartPrepareColorPalette = function() { + var len; + + if(this.colors_custom !== null) return; + + if(this.debug === true) + this.log("Preparing chart color palette"); this.colors = []; this.colors_available = []; + this.colors_custom = []; // add the standard colors - var len = NETDATA.themes.current.colors.length; + len = NETDATA.themes.current.colors.length; while(len--) this.colors_available.unshift(NETDATA.themes.current.colors[len]); // add the user supplied colors - var c = $(this.element).data('colors'); + var c = NETDATA.dataAttribute(this.element, 'colors', undefined); // this.log('read colors: ' + c); - if(typeof c !== 'undefined' && c !== null && c.length > 0) { - if(typeof c !== 'string') { - this.log('invalid color given: ' + c + ' (give a space separated list of colors)'); - } - else { - c = c.split(' '); - var added = 0; - - while(added < 20) { - len = c.length; - while(len--) { - added++; - this.colors_available.unshift(c[len]); - // this.log('adding color: ' + c[len]); - } - } + if(typeof c === 'string' && c.length > 0) { + c = c.split(' '); + len = c.length; + while(len--) { + if(this.debug === true) + this.log("Adding custom color " + c[len].toString() + " to palette"); + + this.colors_custom.unshift(c[len]); + this.colors_available.unshift(c[len]); } } + if(this.debug === true) { + this.log("colors_custom:"); + this.log(this.colors_custom); + this.log("colors_available:"); + this.log(this.colors_available); + } + }; + + // get the ordered list of chart colors + // this includes user defined colors + this.chartCustomColors = function() { + this.chartPrepareColorPalette(); + + var colors; + if(this.colors_custom.length) + colors = this.colors_custom; + else + colors = this.colors; + + if(this.debug === true) { + this.log("chartCustomColors() returns:"); + this.log(colors); + } + + return colors; + }; + + // get the ordered list of chart ASSIGNED colors + // (this returns only the colors that have been + // assigned to dimensions, prepended with any + // custom colors defined) + this.chartColors = function() { + this.chartPrepareColorPalette(); + + if(this.debug === true) { + this.log("chartColors() returns:"); + this.log(this.colors); + } + return this.colors; }; @@ -2555,7 +2890,7 @@ var NETDATA = window.NETDATA || {}; if(needed === false) { // make sure colors available - this.chartColors(); + this.chartPrepareColorPalette(); // do we have to update the current values? // we do this, only when the visible chart is current @@ -2577,12 +2912,12 @@ var NETDATA = window.NETDATA || {}; keys = Object.keys(this.chart.dimensions); len = keys.length; for(i = 0; i < len ;i++) - this._chartDimensionColor(this.chart.dimensions[keys[i]].name); + this.__chartDimensionColor(this.chart.dimensions[keys[i]].name); } } // we will re-generate the colors for the chart - // based on the selected dimensions - this.colors = null; + // based on the dimensions this result has data for + this.colors = []; if(this.debug === true) this.log('updating Legend DOM'); @@ -2591,12 +2926,12 @@ var NETDATA = window.NETDATA || {}; this.dimensions_visibility.invalidateAll(); var genLabel = function(state, parent, dim, name, count) { - var color = state._chartDimensionColor(name); + var color = state.__chartDimensionColor(name); var user_element = null; - var user_id = self.data('show-value-of-' + name.toLowerCase() + '-at') || null; + var user_id = NETDATA.dataAttribute(state.element, 'show-value-of-' + name.toLowerCase() + '-at', null); if(user_id === null) - user_id = self.data('show-value-of-' + dim.toLowerCase() + '-at') || null; + user_id = NETDATA.dataAttribute(state.element, 'show-value-of-' + dim.toLowerCase() + '-at', null); if(user_id !== null) { user_element = document.getElementById(user_id) || null; if (user_element === null) @@ -2635,7 +2970,26 @@ var NETDATA = window.NETDATA || {}; var content = document.createElement('div'); - if(this.hasLegend()) { + if(this.element_chart === null) { + this.element_chart = document.createElement('div'); + this.element_chart.id = this.library_name + '-' + this.uuid + '-chart'; + this.element.appendChild(this.element_chart); + + if(this.hasLegend() === true) + this.element_chart.className = 'netdata-chart-with-legend-right netdata-' + this.library_name + '-chart-with-legend-right'; + else + this.element_chart.className = ' netdata-chart netdata-' + this.library_name + '-chart'; + } + + if(this.hasLegend() === true) { + if(this.element_legend === null) { + this.element_legend = document.createElement('div'); + this.element_legend.className = 'netdata-chart-legend netdata-' + this.library_name + '-legend'; + this.element.appendChild(this.element_legend); + } + else + this.element_legend.innerHTML = ''; + this.element_legend_childs = { content: content, resize_handler: document.createElement('div'), @@ -2653,10 +3007,7 @@ var NETDATA = window.NETDATA || {}; series: {} }; - this.element_legend.innerHTML = ''; - if(this.library.toolboxPanAndZoom !== null) { - var get_pan_and_zoom_step = function(event) { if (event.ctrlKey) return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control; @@ -2717,7 +3068,7 @@ var NETDATA = window.NETDATA || {}; title: 'Chart Reset', content: 'Reset all the charts to their default auto-refreshing state. You can also double click the chart contents with your mouse or your finger (on touch devices).
    Help, can be disabled from the settings.' }); - + this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button'; this.element_legend_childs.toolbox_right.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right); @@ -2741,7 +3092,7 @@ var NETDATA = window.NETDATA || {}; content: 'Pan the chart to the right. You can also drag it with your mouse or your finger (on touch devices).
    Help, can be disabled from the settings.' }); - + this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button'; this.element_legend_childs.toolbox_zoomin.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin); @@ -2763,7 +3114,7 @@ var NETDATA = window.NETDATA || {}; title: 'Chart Zoom In', content: 'Zoom in the chart. You can also press SHIFT and select an area of the chart to zoom in. On Chrome and Opera, you can press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.
    Help, can be disabled from the settings.' }); - + this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button'; this.element_legend_childs.toolbox_zoomout.innerHTML = ''; this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout); @@ -2786,7 +3137,7 @@ var NETDATA = window.NETDATA || {}; title: 'Chart Zoom Out', content: 'Zoom out the chart. On Chrome and Opera, you can also press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.
    Help, can be disabled from the settings.' }); - + //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button'; //this.element_legend_childs.toolbox_volume.innerHTML = ''; //this.element_legend_childs.toolbox_volume.title = 'Visible Volume'; @@ -2805,7 +3156,7 @@ var NETDATA = window.NETDATA || {}; this.element_legend_childs.toolbox_zoomout = null; this.element_legend_childs.toolbox_volume = null; } - + this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler"; this.element_legend_childs.resize_handler.innerHTML = ''; this.element.appendChild(this.element_legend_childs.resize_handler); @@ -2834,19 +3185,19 @@ var NETDATA = window.NETDATA || {}; this.element_legend_childs.title_date.className += " netdata-legend-title-date"; this.element_legend.appendChild(this.element_legend_childs.title_date); - this.__last_shown_legend_date = undefined; + this.tmp.__last_shown_legend_date = undefined; this.element_legend.appendChild(document.createElement('br')); this.element_legend_childs.title_time.className += " netdata-legend-title-time"; this.element_legend.appendChild(this.element_legend_childs.title_time); - this.__last_shown_legend_time = undefined; + this.tmp.__last_shown_legend_time = undefined; this.element_legend.appendChild(document.createElement('br')); this.element_legend_childs.title_units.className += " netdata-legend-title-units"; this.element_legend.appendChild(this.element_legend_childs.title_units); - this.__last_shown_legend_units = undefined; + this.tmp.__last_shown_legend_units = undefined; this.element_legend.appendChild(document.createElement('br')); @@ -2940,16 +3291,14 @@ var NETDATA = window.NETDATA || {}; }; this.hasLegend = function() { - if(typeof this.___hasLegendCache___ !== 'undefined') - return this.___hasLegendCache___; + if(typeof this.tmp.___hasLegendCache___ !== 'undefined') + return this.tmp.___hasLegendCache___; var leg = false; - if(this.library && this.library.legend(this) === 'right-side') { - var legend = $(this.element).data('legend') || 'yes'; - if(legend === 'yes') leg = true; - } + if(this.library && this.library.legend(this) === 'right-side') + leg = NETDATA.dataAttributeBoolean(this.element, 'legend', true); - this.___hasLegendCache___ = leg; + this.tmp.___hasLegendCache___ = leg; return leg; }; @@ -2983,12 +3332,17 @@ var NETDATA = window.NETDATA || {}; }; this.needsRecreation = function() { - return ( + var ret = ( this.chart_created === true && this.library && this.library.autoresize() === false - && this.tm.last_resized < NETDATA.options.last_resized + && this.tm.last_resized < NETDATA.options.last_page_resize ); + + if(this.debug === true) + this.log('needsRecreation(): ' + ret.toString() + ', chart_created = ' + this.chart_created.toString()); + + return ret; }; this.chartURL = function() { @@ -3139,7 +3493,7 @@ var NETDATA = window.NETDATA || {}; if(this.debug === true) this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.'); - init(); + init('force'); return; } @@ -3191,9 +3545,9 @@ var NETDATA = window.NETDATA || {}; this.updateChart = function(callback) { if(this.debug === true) - this.log('updateChart() called.'); + this.log('updateChart()'); - if(this._updating === true) { + if(this.fetching_data === true) { if(this.debug === true) this.log('I am already updating...'); @@ -3222,6 +3576,9 @@ var NETDATA = window.NETDATA || {}; return; } + if(that.dom_created !== true) + createDOM(); + if(this.chart === null) return this.getChart(function() { return that.updateChart(callback); @@ -3255,7 +3612,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max) NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active; - this._updating = true; + this.fetching_data = true; this.xhr = $.ajax( { url: this.data_url, @@ -3296,60 +3653,45 @@ var NETDATA = window.NETDATA || {}; that.xhr = undefined; NETDATA.statistics.refreshes_active--; - that._updating = false; + that.fetching_data = false; if(typeof callback === 'function') return callback(); }); }; - this.isVisible = function(nocache) { - if(typeof nocache === 'undefined') - nocache = false; - - // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll); - - // caching - we do not evaluate the charts visibility - // if the page has not been scrolled since the last check - if(nocache === false && this.tm.last_visible_check > NETDATA.options.last_page_scroll) - return this.___isVisible___; + var __isVisible = function() { + // tolerance is the number of pixels a chart can be off-screen + // to consider it as visible and refresh it as if was visible + var tolerance = 0; - this.tm.last_visible_check = Date.now(); + that.tm.last_visible_check = Date.now(); - var wh = window.innerHeight; - var x = this.element.getBoundingClientRect(); - var ret = 0; - var tolerance = 0; + var rect = that.element.getBoundingClientRect(); - if(x.width === 0 || x.height === 0) { - hideChart(); - this.___isVisible___ = false; - return this.___isVisible___; - } + var screenTop = window.scrollY; + var screenBottom = screenTop + window.innerHeight; - if(x.top < 0 && -x.top > x.height) { - // the chart is entirely above - ret = -x.top - x.height; - } - else if(x.top > wh) { - // the chart is entirely below - ret = x.top - wh; - } + var chartTop = rect.top + screenTop; + var chartBottom = chartTop + rect.height; - if(ret > tolerance) { - // the chart is too far + return !(rect.width === 0 || rect.height === 0 || chartBottom + tolerance < screenTop || chartTop - tolerance > screenBottom); + }; - hideChart(); - this.___isVisible___ = false; - return this.___isVisible___; - } - else { - // the chart is inside or very close + this.isVisible = function(nocache) { + // this.log('last_visible_check: ' + this.tm.last_visible_check + ', last_page_scroll: ' + NETDATA.options.last_page_scroll); - unhideChart(); - this.___isVisible___ = true; - return this.___isVisible___; - } + // caching - we do not evaluate the charts visibility + // if the page has not been scrolled since the last check + if((typeof nocache === 'undefined' || nocache === false) + && typeof this.tmp.___isVisible___ !== 'undefined' + && this.tm.last_visible_check > NETDATA.options.last_page_scroll) + return this.tmp.___isVisible___; + + this.tmp.___isVisible___ = __isVisible(); + if(this.tmp.___isVisible___ === true) unhideChart(); + else hideChart(); + return this.tmp.___isVisible___; }; this.isAutoRefreshable = function() { @@ -3399,8 +3741,8 @@ var NETDATA = window.NETDATA || {}; if(this.isAutoRefreshable() === true) { // allow the first update, even if the page is not visible if(this.updates_counter && this.updates_since_last_unhide && NETDATA.options.page_is_visible === false) { - if(NETDATA.options.debug.focus === true || this.debug === true) - this.log('canBeAutoRefreshed(): page does not have focus'); + // if(NETDATA.options.debug.focus === true || this.debug === true) + // this.log('canBeAutoRefreshed(): page does not have focus'); return false; } @@ -3473,7 +3815,7 @@ var NETDATA = window.NETDATA || {}; } }; - this._defaultsFromDownloadedChart = function(chart) { + this.__defaultsFromDownloadedChart = function(chart) { this.chart = chart; this.chart_url = chart.url; this.data_update_every = chart.update_every * 1000; @@ -3491,7 +3833,7 @@ var NETDATA = window.NETDATA || {}; this.getChart = function(callback) { this.chart = NETDATA.chartRegistry.get(this.host, this.id); if(this.chart) { - this._defaultsFromDownloadedChart(this.chart); + this.__defaultsFromDownloadedChart(this.chart); if(typeof callback === 'function') return callback(); @@ -3510,7 +3852,7 @@ var NETDATA = window.NETDATA || {}; }) .done(function(chart) { chart.url = that.chart_url; - that._defaultsFromDownloadedChart(chart); + that.__defaultsFromDownloadedChart(chart); NETDATA.chartRegistry.add(that.host, that.id, chart); }) .fail(function() { @@ -3527,7 +3869,8 @@ var NETDATA = window.NETDATA || {}; // ============================================================================================================ // INITIALIZATION - init(); + initDOM(); + init('fast'); }; NETDATA.resetAllCharts = function(state) { @@ -3556,10 +3899,12 @@ var NETDATA = window.NETDATA || {}; // get or create a chart state, given a DOM element NETDATA.chartState = function(element) { - var state = $(element).data('netdata-state-object') || null; + var self = $(element); + + var state = self.data('netdata-state-object') || null; if(state === null) { state = new chartState(element); - $(element).data('netdata-state-object', state); + self.data('netdata-state-object', state); } return state; }; @@ -3710,9 +4055,11 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.main_loop === true) console.log('fast rendering...'); - state.autoRefresh(function() { - NETDATA.chartRefresherNoParallel(++index); - }); + setTimeout(function() { + state.autoRefresh(function () { + NETDATA.chartRefresherNoParallel(++index); + }); + }, 0); } else { if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...'); @@ -3783,7 +4130,10 @@ var NETDATA = window.NETDATA || {}; } } - parallel.unshift(state); + if(NETDATA.scrollUp === true) + parallel.unshift(state); + else + parallel.push(state); } if(parallel.length > 0) { @@ -3911,12 +4261,12 @@ var NETDATA = window.NETDATA || {}; NETDATA.peityChartUpdate = function(state, data) { state.peity_instance.innerHTML = data.result; - if(state.peity_options.stroke !== state.chartColors()[0]) { - state.peity_options.stroke = state.chartColors()[0]; + if(state.peity_options.stroke !== state.chartCustomColors()[0]) { + state.peity_options.stroke = state.chartCustomColors()[0]; if(state.chart.chart_type === 'line') state.peity_options.fill = NETDATA.themes.current.background; else - state.peity_options.fill = NETDATA.colorLuminance(state.chartColors()[0], NETDATA.chartDefaults.fill_luminance); + state.peity_options.fill = NETDATA.colorLuminance(state.chartCustomColors()[0], NETDATA.chartDefaults.fill_luminance); } $(state.peity_instance).peity('line', state.peity_options); @@ -3927,10 +4277,9 @@ var NETDATA = window.NETDATA || {}; state.peity_instance = document.createElement('div'); state.element_chart.appendChild(state.peity_instance); - var self = $(state.element); state.peity_options = { stroke: NETDATA.themes.current.foreground, - strokeWidth: self.data('peity-strokewidth') || 1, + strokeWidth: NETDATA.dataAttribute(state.element, 'peity-strokewidth', 1), width: state.chartWidth(), height: state.chartHeight(), fill: NETDATA.themes.current.foreground @@ -3979,52 +4328,51 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.sparklineChartCreate = function(state, data) { - var self = $(state.element); - var type = self.data('sparkline-type') || 'line'; - var lineColor = self.data('sparkline-linecolor') || state.chartColors()[0]; - var fillColor = self.data('sparkline-fillcolor') || ((state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance)); - var chartRangeMin = self.data('sparkline-chartrangemin') || undefined; - var chartRangeMax = self.data('sparkline-chartrangemax') || undefined; - var composite = self.data('sparkline-composite') || undefined; - var enableTagOptions = self.data('sparkline-enabletagoptions') || undefined; - var tagOptionPrefix = self.data('sparkline-tagoptionprefix') || undefined; - var tagValuesAttribute = self.data('sparkline-tagvaluesattribute') || undefined; - var disableHiddenCheck = self.data('sparkline-disablehiddencheck') || undefined; - var defaultPixelsPerValue = self.data('sparkline-defaultpixelspervalue') || undefined; - var spotColor = self.data('sparkline-spotcolor') || undefined; - var minSpotColor = self.data('sparkline-minspotcolor') || undefined; - var maxSpotColor = self.data('sparkline-maxspotcolor') || undefined; - var spotRadius = self.data('sparkline-spotradius') || undefined; - var valueSpots = self.data('sparkline-valuespots') || undefined; - var highlightSpotColor = self.data('sparkline-highlightspotcolor') || undefined; - var highlightLineColor = self.data('sparkline-highlightlinecolor') || undefined; - var lineWidth = self.data('sparkline-linewidth') || undefined; - var normalRangeMin = self.data('sparkline-normalrangemin') || undefined; - var normalRangeMax = self.data('sparkline-normalrangemax') || undefined; - var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined; - var xvalues = self.data('sparkline-xvalues') || undefined; - var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined; - var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined; - var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined; - var disableInteraction = self.data('sparkline-disableinteraction') || false; - var disableTooltips = self.data('sparkline-disabletooltips') || false; - var disableHighlight = self.data('sparkline-disablehighlight') || false; - var highlightLighten = self.data('sparkline-highlightlighten') || 1.4; - var highlightColor = self.data('sparkline-highlightcolor') || undefined; - var tooltipContainer = self.data('sparkline-tooltipcontainer') || undefined; - var tooltipClassname = self.data('sparkline-tooltipclassname') || undefined; - var tooltipFormat = self.data('sparkline-tooltipformat') || undefined; - var tooltipPrefix = self.data('sparkline-tooltipprefix') || undefined; - var tooltipSuffix = self.data('sparkline-tooltipsuffix') || ' ' + state.units; - var tooltipSkipNull = self.data('sparkline-tooltipskipnull') || true; - var tooltipValueLookups = self.data('sparkline-tooltipvaluelookups') || undefined; - var tooltipFormatFieldlist = self.data('sparkline-tooltipformatfieldlist') || undefined; - var tooltipFormatFieldlistKey = self.data('sparkline-tooltipformatfieldlistkey') || undefined; - var numberFormatter = self.data('sparkline-numberformatter') || function(n){ return n.toFixed(2); }; - var numberDigitGroupSep = self.data('sparkline-numberdigitgroupsep') || undefined; - var numberDecimalMark = self.data('sparkline-numberdecimalmark') || undefined; - var numberDigitGroupCount = self.data('sparkline-numberdigitgroupcount') || undefined; - var animatedZooms = self.data('sparkline-animatedzooms') || false; + var type = NETDATA.dataAttribute(state.element, 'sparkline-type', 'line'); + var lineColor = NETDATA.dataAttribute(state.element, 'sparkline-linecolor', state.chartCustomColors()[0]); + var fillColor = NETDATA.dataAttribute(state.element, 'sparkline-fillcolor', ((state.chart.chart_type === 'line')?NETDATA.themes.current.background:NETDATA.colorLuminance(lineColor, NETDATA.chartDefaults.fill_luminance))); + var chartRangeMin = NETDATA.dataAttribute(state.element, 'sparkline-chartrangemin', undefined); + var chartRangeMax = NETDATA.dataAttribute(state.element, 'sparkline-chartrangemax', undefined); + var composite = NETDATA.dataAttribute(state.element, 'sparkline-composite', undefined); + var enableTagOptions = NETDATA.dataAttribute(state.element, 'sparkline-enabletagoptions', undefined); + var tagOptionPrefix = NETDATA.dataAttribute(state.element, 'sparkline-tagoptionprefix', undefined); + var tagValuesAttribute = NETDATA.dataAttribute(state.element, 'sparkline-tagvaluesattribute', undefined); + var disableHiddenCheck = NETDATA.dataAttribute(state.element, 'sparkline-disablehiddencheck', undefined); + var defaultPixelsPerValue = NETDATA.dataAttribute(state.element, 'sparkline-defaultpixelspervalue', undefined); + var spotColor = NETDATA.dataAttribute(state.element, 'sparkline-spotcolor', undefined); + var minSpotColor = NETDATA.dataAttribute(state.element, 'sparkline-minspotcolor', undefined); + var maxSpotColor = NETDATA.dataAttribute(state.element, 'sparkline-maxspotcolor', undefined); + var spotRadius = NETDATA.dataAttribute(state.element, 'sparkline-spotradius', undefined); + var valueSpots = NETDATA.dataAttribute(state.element, 'sparkline-valuespots', undefined); + var highlightSpotColor = NETDATA.dataAttribute(state.element, 'sparkline-highlightspotcolor', undefined); + var highlightLineColor = NETDATA.dataAttribute(state.element, 'sparkline-highlightlinecolor', undefined); + var lineWidth = NETDATA.dataAttribute(state.element, 'sparkline-linewidth', undefined); + var normalRangeMin = NETDATA.dataAttribute(state.element, 'sparkline-normalrangemin', undefined); + var normalRangeMax = NETDATA.dataAttribute(state.element, 'sparkline-normalrangemax', undefined); + var drawNormalOnTop = NETDATA.dataAttribute(state.element, 'sparkline-drawnormalontop', undefined); + var xvalues = NETDATA.dataAttribute(state.element, 'sparkline-xvalues', undefined); + var chartRangeClip = NETDATA.dataAttribute(state.element, 'sparkline-chartrangeclip', undefined); + var chartRangeMinX = NETDATA.dataAttribute(state.element, 'sparkline-chartrangeminx', undefined); + var chartRangeMaxX = NETDATA.dataAttribute(state.element, 'sparkline-chartrangemaxx', undefined); + var disableInteraction = NETDATA.dataAttributeBoolean(state.element, 'sparkline-disableinteraction', false); + var disableTooltips = NETDATA.dataAttributeBoolean(state.element, 'sparkline-disabletooltips', false); + var disableHighlight = NETDATA.dataAttributeBoolean(state.element, 'sparkline-disablehighlight', false); + var highlightLighten = NETDATA.dataAttribute(state.element, 'sparkline-highlightlighten', 1.4); + var highlightColor = NETDATA.dataAttribute(state.element, 'sparkline-highlightcolor', undefined); + var tooltipContainer = NETDATA.dataAttribute(state.element, 'sparkline-tooltipcontainer', undefined); + var tooltipClassname = NETDATA.dataAttribute(state.element, 'sparkline-tooltipclassname', undefined); + var tooltipFormat = NETDATA.dataAttribute(state.element, 'sparkline-tooltipformat', undefined); + var tooltipPrefix = NETDATA.dataAttribute(state.element, 'sparkline-tooltipprefix', undefined); + var tooltipSuffix = NETDATA.dataAttribute(state.element, 'sparkline-tooltipsuffix', ' ' + state.units); + var tooltipSkipNull = NETDATA.dataAttributeBoolean(state.element, 'sparkline-tooltipskipnull', true); + var tooltipValueLookups = NETDATA.dataAttribute(state.element, 'sparkline-tooltipvaluelookups', undefined); + var tooltipFormatFieldlist = NETDATA.dataAttribute(state.element, 'sparkline-tooltipformatfieldlist', undefined); + var tooltipFormatFieldlistKey = NETDATA.dataAttribute(state.element, 'sparkline-tooltipformatfieldlistkey', undefined); + var numberFormatter = NETDATA.dataAttribute(state.element, 'sparkline-numberformatter', function(n){ return n.toFixed(2); }); + var numberDigitGroupSep = NETDATA.dataAttribute(state.element, 'sparkline-numberdigitgroupsep', undefined); + var numberDecimalMark = NETDATA.dataAttribute(state.element, 'sparkline-numberdecimalmark', undefined); + var numberDigitGroupCount = NETDATA.dataAttribute(state.element, 'sparkline-numberdigitgroupcount', undefined); + var animatedZooms = NETDATA.dataAttributeBoolean(state.element, 'sparkline-animatedzooms', false); if(spotColor === 'disable') spotColor=''; if(minSpotColor === 'disable') minSpotColor=''; @@ -4104,19 +4452,19 @@ var NETDATA = window.NETDATA || {}; state.setMode('zoom'); state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); - state.dygraph_user_action = true; - state.dygraph_force_zoom = true; + state.tmp.dygraph_user_action = true; + state.tmp.dygraph_force_zoom = true; state.updateChartPanOrZoom(after, before); NETDATA.globalPanAndZoom.setMaster(state, after, before); }; NETDATA.dygraphSetSelection = function(state, t) { - if(typeof state.dygraph_instance !== 'undefined') { + if(typeof state.tmp.dygraph_instance !== 'undefined') { var r = state.calculateRowForTime(t); if(r !== -1) - state.dygraph_instance.setSelection(r); + state.tmp.dygraph_instance.setSelection(r); else { - state.dygraph_instance.clearSelection(); + state.tmp.dygraph_instance.clearSelection(); state.legendShowUndefined(); } } @@ -4125,8 +4473,8 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.dygraphClearSelection = function(state) { - if(typeof state.dygraph_instance !== 'undefined') { - state.dygraph_instance.clearSelection(); + if(typeof state.tmp.dygraph_instance !== 'undefined') { + state.tmp.dygraph_instance.clearSelection(); } return true; }; @@ -4181,7 +4529,7 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.dygraphChartUpdate = function(state, data) { - var dygraph = state.dygraph_instance; + var dygraph = state.tmp.dygraph_instance; if(typeof dygraph === 'undefined') return NETDATA.dygraphChartCreate(state, data); @@ -4191,7 +4539,7 @@ var NETDATA = window.NETDATA || {}; // its element size as 0x0. // this will make it re-appear properly - if(state.tm.last_unhidden > state.dygraph_last_rendered) + if(state.tm.last_unhidden > state.tmp.dygraph_last_rendered) dygraph.resize(); var options = { @@ -4202,13 +4550,13 @@ var NETDATA = window.NETDATA || {}; visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names) }; - if(state.dygraph_force_zoom === true) { + if(state.tmp.dygraph_force_zoom === true) { if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphChartUpdate() forced zoom update'); options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null; options.isZoomedIgnoreProgrammaticZoom = true; - state.dygraph_force_zoom = false; + state.tmp.dygraph_force_zoom = false; } else if(state.current.name !== 'auto') { if(NETDATA.options.debug.dygraph === true || state.debug === true) @@ -4222,21 +4570,21 @@ var NETDATA = window.NETDATA || {}; options.isZoomedIgnoreProgrammaticZoom = true; } - options.valueRange = state.dygraph_options.valueRange; + options.valueRange = state.tmp.dygraph_options.valueRange; var oldMax = null, oldMin = null; - if(state.__commonMin !== null) { - state.data.min = state.dygraph_instance.axes_[0].extremeRange[0]; + if (state.tmp.__commonMin !== null) { + state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0]; oldMin = options.valueRange[0] = NETDATA.commonMin.get(state); } - if(state.__commonMax !== null) { - state.data.max = state.dygraph_instance.axes_[0].extremeRange[1]; + if (state.tmp.__commonMax !== null) { + state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1]; oldMax = options.valueRange[1] = NETDATA.commonMax.get(state); } - if(state.dygraph_smooth_eligible === true) { - if((NETDATA.options.current.smooth_plot === true && state.dygraph_options.plotter !== smoothPlotter) - || (NETDATA.options.current.smooth_plot === false && state.dygraph_options.plotter === smoothPlotter)) { + if(state.tmp.dygraph_smooth_eligible === true) { + if((NETDATA.options.current.smooth_plot === true && state.tmp.dygraph_options.plotter !== smoothPlotter) + || (NETDATA.options.current.smooth_plot === false && state.tmp.dygraph_options.plotter === smoothPlotter)) { NETDATA.dygraphChartCreate(state, data); return; } @@ -4245,13 +4593,13 @@ var NETDATA = window.NETDATA || {}; dygraph.updateOptions(options); var redraw = false; - if(oldMin !== null && oldMin > state.dygraph_instance.axes_[0].extremeRange[0]) { - state.data.min = state.dygraph_instance.axes_[0].extremeRange[0]; + if(oldMin !== null && oldMin > state.tmp.dygraph_instance.axes_[0].extremeRange[0]) { + state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0]; options.valueRange[0] = NETDATA.commonMin.get(state); redraw = true; } - if(oldMax !== null && oldMax < state.dygraph_instance.axes_[0].extremeRange[1]) { - state.data.max = state.dygraph_instance.axes_[0].extremeRange[1]; + if(oldMax !== null && oldMax < state.tmp.dygraph_instance.axes_[0].extremeRange[1]) { + state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1]; options.valueRange[1] = NETDATA.commonMax.get(state); redraw = true; } @@ -4261,7 +4609,7 @@ var NETDATA = window.NETDATA || {}; dygraph.updateOptions(options); } - state.dygraph_last_rendered = Date.now(); + state.tmp.dygraph_last_rendered = Date.now(); return true; }; @@ -4269,185 +4617,93 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('dygraphChartCreate()'); - var self = $(state.element); - - var chart_type = self.data('dygraph-type') || state.chart.chart_type; + var chart_type = NETDATA.dataAttribute(state.element, 'dygraph-type', state.chart.chart_type); if(chart_type === 'stacked' && data.dimensions === 1) chart_type = 'area'; var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state) === true)?3:4; var smooth = (NETDATA.dygraph.smooth === true) - ?(self.data('dygraph-smooth') || (chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false)) + ?(NETDATA.dataAttributeBoolean(state.element, 'dygraph-smooth', (chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false))) :false; - state.dygraph_options = { - colors: self.data('dygraph-colors') || state.chartColors(), + state.tmp.dygraph_options = { + colors: NETDATA.dataAttribute(state.element, 'dygraph-colors', state.chartColors()), // leave a few pixels empty on the right of the chart - rightGap: self.data('dygraph-rightgap') - || 5, - - showRangeSelector: self.data('dygraph-showrangeselector') - || false, - - showRoller: self.data('dygraph-showroller') - || false, - - title: self.data('dygraph-title') - || state.title, - - titleHeight: self.data('dygraph-titleheight') - || 19, - - legend: self.data('dygraph-legend') - || 'always', // we need this to get selection events - + rightGap: NETDATA.dataAttribute(state.element, 'dygraph-rightgap', 5), + showRangeSelector: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showrangeselector', false), + showRoller: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showroller', false), + title: NETDATA.dataAttribute(state.element, 'dygraph-title', state.title), + titleHeight: NETDATA.dataAttribute(state.element, 'dygraph-titleheight', 19), + legend: NETDATA.dataAttribute(state.element, 'dygraph-legend', 'always'), // we need this to get selection events labels: data.result.labels, - - labelsDiv: self.data('dygraph-labelsdiv') - || state.element_legend_childs.hidden, - - labelsDivStyles: self.data('dygraph-labelsdivstyles') - || { 'fontSize':'1px' }, - - labelsDivWidth: self.data('dygraph-labelsdivwidth') - || state.chartWidth() - 70, - - labelsSeparateLines: self.data('dygraph-labelsseparatelines') - || true, - - labelsShowZeroValues: self.data('dygraph-labelsshowzerovalues') - || true, - + labelsDiv: NETDATA.dataAttribute(state.element, 'dygraph-labelsdiv', state.element_legend_childs.hidden), + labelsDivStyles: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivstyles', { 'fontSize':'1px' }), + labelsDivWidth: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivwidth', state.chartWidth() - 70), + labelsSeparateLines: NETDATA.dataAttributeBoolean(state.element, 'dygraph-labelsseparatelines', true), + labelsShowZeroValues: NETDATA.dataAttributeBoolean(state.element, 'dygraph-labelsshowzerovalues', true), labelsKMB: false, labelsKMG2: false, - - showLabelsOnHighlight: self.data('dygraph-showlabelsonhighlight') - || true, - - hideOverlayOnMouseOut: self.data('dygraph-hideoverlayonmouseout') - || true, - - includeZero: self.data('dygraph-includezero') - || (chart_type === 'stacked'), - - xRangePad: self.data('dygraph-xrangepad') - || 0, - - yRangePad: self.data('dygraph-yrangepad') - || 1, - - valueRange: self.data('dygraph-valuerange') - || [ null, null ], - + showLabelsOnHighlight: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showlabelsonhighlight', true), + hideOverlayOnMouseOut: NETDATA.dataAttributeBoolean(state.element, 'dygraph-hideoverlayonmouseout', true), + includeZero: NETDATA.dataAttribute(state.element, 'dygraph-includezero', (chart_type === 'stacked')), + xRangePad: NETDATA.dataAttribute(state.element, 'dygraph-xrangepad', 0), + yRangePad: NETDATA.dataAttribute(state.element, 'dygraph-yrangepad', 1), + valueRange: NETDATA.dataAttribute(state.element, 'dygraph-valuerange', [ null, null ]), ylabel: state.units, - - yLabelWidth: self.data('dygraph-ylabelwidth') - || 12, + yLabelWidth: NETDATA.dataAttribute(state.element, 'dygraph-ylabelwidth', 12), // the function to plot the chart plotter: null, // The width of the lines connecting data points. // This can be used to increase the contrast or some graphs. - strokeWidth: self.data('dygraph-strokewidth') - || ((chart_type === 'stacked')?0.1:((smooth === true)?1.5:0.7)), - - strokePattern: self.data('dygraph-strokepattern') - || undefined, + strokeWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokewidth', ((chart_type === 'stacked')?0.1:((smooth === true)?1.5:0.7))), + strokePattern: NETDATA.dataAttribute(state.element, 'dygraph-strokepattern', undefined), // The size of the dot to draw on each point in pixels (see drawPoints). // A dot is always drawn when a point is "isolated", // i.e. there is a missing point on either side of it. // This also controls the size of those dots. - drawPoints: self.data('dygraph-drawpoints') - || false, + drawPoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawpoints', false), // Draw points at the edges of gaps in the data. // This improves visibility of small data segments or other data irregularities. - drawGapEdgePoints: self.data('dygraph-drawgapedgepoints') - || true, - - connectSeparatedPoints: self.data('dygraph-connectseparatedpoints') - || false, - - pointSize: self.data('dygraph-pointsize') - || 1, + drawGapEdgePoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgapedgepoints', true), + connectSeparatedPoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-connectseparatedpoints', false), + pointSize: NETDATA.dataAttribute(state.element, 'dygraph-pointsize', 1), // enabling this makes the chart with little square lines - stepPlot: self.data('dygraph-stepplot') - || false, + stepPlot: NETDATA.dataAttributeBoolean(state.element, 'dygraph-stepplot', false), // Draw a border around graph lines to make crossing lines more easily // distinguishable. Useful for graphs with many lines. - strokeBorderColor: self.data('dygraph-strokebordercolor') - || NETDATA.themes.current.background, - - strokeBorderWidth: self.data('dygraph-strokeborderwidth') - || (chart_type === 'stacked')?0.0:0.0, - - fillGraph: self.data('dygraph-fillgraph') - || (chart_type === 'area' || chart_type === 'stacked'), - - fillAlpha: self.data('dygraph-fillalpha') - || ((chart_type === 'stacked') + strokeBorderColor: NETDATA.dataAttribute(state.element, 'dygraph-strokebordercolor', NETDATA.themes.current.background), + strokeBorderWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokeborderwidth', (chart_type === 'stacked')?0.0:0.0), + fillGraph: NETDATA.dataAttribute(state.element, 'dygraph-fillgraph', (chart_type === 'area' || chart_type === 'stacked')), + fillAlpha: NETDATA.dataAttribute(state.element, 'dygraph-fillalpha', + ((chart_type === 'stacked') ?NETDATA.options.current.color_fill_opacity_stacked - :NETDATA.options.current.color_fill_opacity_area), - - stackedGraph: self.data('dygraph-stackedgraph') - || (chart_type === 'stacked'), - - stackedGraphNaNFill: self.data('dygraph-stackedgraphnanfill') - || 'none', - - drawAxis: self.data('dygraph-drawaxis') - || true, - - axisLabelFontSize: self.data('dygraph-axislabelfontsize') - || 10, - - axisLineColor: self.data('dygraph-axislinecolor') - || NETDATA.themes.current.axis, - - axisLineWidth: self.data('dygraph-axislinewidth') - || 1.0, - - drawGrid: self.data('dygraph-drawgrid') - || true, - - gridLinePattern: self.data('dygraph-gridlinepattern') - || null, - - gridLineWidth: self.data('dygraph-gridlinewidth') - || 1.0, - - gridLineColor: self.data('dygraph-gridlinecolor') - || NETDATA.themes.current.grid, - - maxNumberWidth: self.data('dygraph-maxnumberwidth') - || 8, - - sigFigs: self.data('dygraph-sigfigs') - || null, - - digitsAfterDecimal: self.data('dygraph-digitsafterdecimal') - || 2, - - valueFormatter: self.data('dygraph-valueformatter') - || undefined, - - highlightCircleSize: self.data('dygraph-highlightcirclesize') - || highlightCircleSize, - - highlightSeriesOpts: self.data('dygraph-highlightseriesopts') - || null, // TOO SLOW: { strokeWidth: 1.5 }, - - highlightSeriesBackgroundAlpha: self.data('dygraph-highlightseriesbackgroundalpha') - || null, // TOO SLOW: (chart_type === 'stacked')?0.7:0.5, - - pointClickCallback: self.data('dygraph-pointclickcallback') - || undefined, - + :NETDATA.options.current.color_fill_opacity_area) + ), + stackedGraph: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraph', (chart_type === 'stacked')), + stackedGraphNaNFill: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraphnanfill', 'none'), + drawAxis: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawaxis', true), + axisLabelFontSize: NETDATA.dataAttribute(state.element, 'dygraph-axislabelfontsize', 10), + axisLineColor: NETDATA.dataAttribute(state.element, 'dygraph-axislinecolor', NETDATA.themes.current.axis), + axisLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-axislinewidth', 1.0), + drawGrid: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgrid', true), + gridLinePattern: NETDATA.dataAttribute(state.element, 'dygraph-gridlinepattern', null), + gridLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-gridlinewidth', 1.0), + gridLineColor: NETDATA.dataAttribute(state.element, 'dygraph-gridlinecolor', NETDATA.themes.current.grid), + maxNumberWidth: NETDATA.dataAttribute(state.element, 'dygraph-maxnumberwidth', 8), + sigFigs: NETDATA.dataAttribute(state.element, 'dygraph-sigfigs', null), + digitsAfterDecimal: NETDATA.dataAttribute(state.element, 'dygraph-digitsafterdecimal', 2), + valueFormatter: NETDATA.dataAttribute(state.element, 'dygraph-valueformatter', undefined), + highlightCircleSize: NETDATA.dataAttribute(state.element, 'dygraph-highlightcirclesize', highlightCircleSize), + highlightSeriesOpts: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesopts', null), // TOO SLOW: { strokeWidth: 1.5 }, + highlightSeriesBackgroundAlpha: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesbackgroundalpha', null), // TOO SLOW: (chart_type === 'stacked')?0.7:0.5, + pointClickCallback: NETDATA.dataAttribute(state.element, 'dygraph-pointclickcallback', undefined), visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names), axes: { @@ -4495,8 +4751,8 @@ var NETDATA = window.NETDATA || {}; return ''; }, drawCallback: function(dygraph, is_initial) { - if(state.current.name !== 'auto' && state.dygraph_user_action === true) { - state.dygraph_user_action = false; + if(state.current.name !== 'auto' && state.tmp.dygraph_user_action === true) { + state.tmp.dygraph_user_action = false; var x_range = dygraph.xAxisRange(); var after = Math.round(x_range[0]); @@ -4520,8 +4776,8 @@ var NETDATA = window.NETDATA || {}; state.setMode('zoom'); // refresh it to the greatest possible zoom level - state.dygraph_user_action = true; - state.dygraph_force_zoom = true; + state.tmp.dygraph_user_action = true; + state.tmp.dygraph_force_zoom = true; state.updateChartPanOrZoom(minDate, maxDate); }, highlightCallback: function(event, x, points, row, seriesName) { @@ -4543,7 +4799,7 @@ var NETDATA = window.NETDATA || {}; // fix legend zIndex using the internal structures of dygraph legend module // this works, but it is a hack! - // state.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000; + // state.tmp.dygraph_instance.plugins_[0].plugin.legend_div_.style.zIndex = 10000; }, unhighlightCallback: function(event) { void(event); @@ -4559,7 +4815,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.mousedown()'); - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncStop(); if(NETDATA.options.debug.dygraph === true) @@ -4600,7 +4856,7 @@ var NETDATA = window.NETDATA || {}; state.log('interactionModel.mousemove()'); if(context.isPanning) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); state.setMode('pan'); @@ -4608,7 +4864,7 @@ var NETDATA = window.NETDATA || {}; Dygraph.movePan(event, dygraph, context); } else if(context.isZooming) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); state.setMode('zoom'); @@ -4620,12 +4876,12 @@ var NETDATA = window.NETDATA || {}; state.log('interactionModel.mouseup()'); if (context.isPanning) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncDelay(); Dygraph.endPan(event, dygraph, context); } else if (context.isZooming) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncDelay(); Dygraph.endZoom(event, dygraph, context); } @@ -4714,7 +4970,7 @@ var NETDATA = window.NETDATA || {}; } if(event.altKey || event.shiftKey) { - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.globalSelectionSyncStop(); state.globalSelectionSyncDelay(); @@ -4766,7 +5022,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.touchstart()'); - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; state.setMode('zoom'); state.pauseChart(); @@ -4788,7 +5044,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.touchmove()'); - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; Dygraph.defaultInteractionModel.touchmove(event, dygraph, context); state.dygraph_last_touch_move = Date.now(); @@ -4797,7 +5053,7 @@ var NETDATA = window.NETDATA || {}; if(NETDATA.options.debug.dygraph === true || state.debug === true) state.log('interactionModel.touchend()'); - state.dygraph_user_action = true; + state.tmp.dygraph_user_action = true; Dygraph.defaultInteractionModel.touchend(event, dygraph, context); // if it didn't move, it is a selection @@ -4826,41 +5082,48 @@ var NETDATA = window.NETDATA || {}; }; if(NETDATA.chartLibraries.dygraph.isSparkline(state)) { - state.dygraph_options.drawGrid = false; - state.dygraph_options.drawAxis = false; - state.dygraph_options.title = undefined; - state.dygraph_options.ylabel = undefined; - state.dygraph_options.yLabelWidth = 0; - state.dygraph_options.labelsDivWidth = 120; - state.dygraph_options.labelsDivStyles.width = '120px'; - state.dygraph_options.labelsSeparateLines = true; - state.dygraph_options.rightGap = 0; - state.dygraph_options.yRangePad = 1; + state.tmp.dygraph_options.drawGrid = false; + state.tmp.dygraph_options.drawAxis = false; + state.tmp.dygraph_options.title = undefined; + state.tmp.dygraph_options.ylabel = undefined; + state.tmp.dygraph_options.yLabelWidth = 0; + state.tmp.dygraph_options.labelsDivWidth = 120; + state.tmp.dygraph_options.labelsDivStyles.width = '120px'; + state.tmp.dygraph_options.labelsSeparateLines = true; + state.tmp.dygraph_options.rightGap = 0; + state.tmp.dygraph_options.yRangePad = 1; } if(smooth === true) { - state.dygraph_smooth_eligible = true; + state.tmp.dygraph_smooth_eligible = true; if(NETDATA.options.current.smooth_plot === true) - state.dygraph_options.plotter = smoothPlotter; + state.tmp.dygraph_options.plotter = smoothPlotter; } - else state.dygraph_smooth_eligible = false; + else state.tmp.dygraph_smooth_eligible = false; - state.dygraph_instance = new Dygraph(state.element_chart, - data.result.data, state.dygraph_options); + state.tmp.dygraph_instance = new Dygraph(state.element_chart, + data.result.data, state.tmp.dygraph_options); - state.dygraph_force_zoom = false; - state.dygraph_user_action = false; - state.dygraph_last_rendered = Date.now(); + state.tmp.dygraph_force_zoom = false; + state.tmp.dygraph_user_action = false; + state.tmp.dygraph_last_rendered = Date.now(); - if(typeof state.dygraph_instance.axes_[0].extremeRange !== 'undefined') { - state.__commonMin = self.data('common-min') || null; - state.__commonMax = self.data('common-max') || null; + if(state.tmp.dygraph_options.valueRange[0] === null && state.tmp.dygraph_options.valueRange[1] === null) { + if (typeof state.tmp.dygraph_instance.axes_[0].extremeRange !== 'undefined') { + state.tmp.__commonMin = NETDATA.dataAttribute(state.element, 'common-min', null); + state.tmp.__commonMax = NETDATA.dataAttribute(state.element, 'common-max', null); + } + else { + state.log('incompatible version of Dygraph detected'); + state.tmp.__commonMin = null; + state.tmp.__commonMax = null; + } } else { - state.log('incompatible version of Dygraph detected'); - state.__commonMin = null; - state.__commonMax = null; + // if the user gave a valueRange, respect it + state.tmp.__commonMin = null; + state.tmp.__commonMax = null; } return true; @@ -5289,7 +5552,7 @@ var NETDATA = window.NETDATA || {}; // ---------------------------------------------------------------------------------------------------------------- - NETDATA.easypiechartPercentFromValueMinMax = function(value, min, max) { + NETDATA.easypiechartPercentFromValueMinMax = function(state, value, min, max) { if(typeof value !== 'number') value = 0; if(typeof min !== 'number') min = 0; if(typeof max !== 'number') max = 0; @@ -5298,8 +5561,9 @@ var NETDATA = window.NETDATA || {}; if(max < value) max = value; // make sure it is zero based - if(min > 0) min = 0; - if(max < 0) max = 0; + // but only they have not been set by the user + if(state.tmp.easyPieChartMin === null && min > 0) min = 0; + if(state.tmp.easyPieChartMax === null && max < 0) max = 0; var pcent = 0; if(value >= 0) { @@ -5347,22 +5611,22 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.easypiechartClearSelection = function(state) { - if(typeof state.easyPieChartEvent !== 'undefined') { - if(state.easyPieChartEvent.timer !== undefined) { - clearTimeout(state.easyPieChartEvent.timer); + if(typeof state.tmp.easyPieChartEvent !== 'undefined') { + if(state.tmp.easyPieChartEvent.timer !== undefined) { + clearTimeout(state.tmp.easyPieChartEvent.timer); } - state.easyPieChartEvent.timer = undefined; + state.tmp.easyPieChartEvent.timer = undefined; } if(state.isAutoRefreshable() === true && state.data !== null) { NETDATA.easypiechartChartUpdate(state, state.data); } else { - state.easyPieChartLabel.innerText = state.legendFormatValue(null); - state.easyPieChart_instance.update(0); + state.tmp.easyPieChartLabel.innerText = state.legendFormatValue(null); + state.tmp.easyPieChart_instance.update(0); } - state.easyPieChart_instance.enableAnimation(); + state.tmp.easyPieChart_instance.enableAnimation(); return true; }; @@ -5375,8 +5639,8 @@ var NETDATA = window.NETDATA || {}; if(slot < 0 || slot >= state.data.result.length) return NETDATA.easypiechartClearSelection(state); - if(typeof state.easyPieChartEvent === 'undefined') { - state.easyPieChartEvent = { + if(typeof state.tmp.easyPieChartEvent === 'undefined') { + state.tmp.easyPieChartEvent = { timer: undefined, value: 0, pcent: 0 @@ -5384,20 +5648,20 @@ var NETDATA = window.NETDATA || {}; } var value = state.data.result[state.data.result.length - 1 - slot]; - var min = (state.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.easyPieChartMin; - var max = (state.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.easyPieChartMax; - var pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); + var min = (state.tmp.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.tmp.easyPieChartMin; + var max = (state.tmp.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.tmp.easyPieChartMax; + var pcent = NETDATA.easypiechartPercentFromValueMinMax(state, value, min, max); - state.easyPieChartEvent.value = value; - state.easyPieChartEvent.pcent = pcent; - state.easyPieChartLabel.innerText = state.legendFormatValue(value); + state.tmp.easyPieChartEvent.value = value; + state.tmp.easyPieChartEvent.pcent = pcent; + state.tmp.easyPieChartLabel.innerText = state.legendFormatValue(value); - if(state.easyPieChartEvent.timer === undefined) { - state.easyPieChart_instance.disableAnimation(); + if(state.tmp.easyPieChartEvent.timer === undefined) { + state.tmp.easyPieChart_instance.disableAnimation(); - state.easyPieChartEvent.timer = setTimeout(function() { - state.easyPieChartEvent.timer = undefined; - state.easyPieChart_instance.update(state.easyPieChartEvent.pcent); + state.tmp.easyPieChartEvent.timer = setTimeout(function() { + state.tmp.easyPieChartEvent.timer = undefined; + state.tmp.easyPieChart_instance.update(state.tmp.easyPieChartEvent.pcent); }, NETDATA.options.current.charts_selection_animation_delay); } @@ -5413,88 +5677,76 @@ var NETDATA = window.NETDATA || {}; } else { value = data.result[0]; - min = (state.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.easyPieChartMin; - max = (state.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.easyPieChartMax; - pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); + min = (state.tmp.easyPieChartMin === null)?NETDATA.commonMin.get(state):state.tmp.easyPieChartMin; + max = (state.tmp.easyPieChartMax === null)?NETDATA.commonMax.get(state):state.tmp.easyPieChartMax; + pcent = NETDATA.easypiechartPercentFromValueMinMax(state, value, min, max); } - state.easyPieChartLabel.innerText = state.legendFormatValue(value); - state.easyPieChart_instance.update(pcent); + state.tmp.easyPieChartLabel.innerText = state.legendFormatValue(value); + state.tmp.easyPieChart_instance.update(pcent); return true; }; NETDATA.easypiechartChartCreate = function(state, data) { - var self = $(state.element); var chart = $(state.element_chart); var value = data.result[0]; - var min = self.data('easypiechart-min-value') || null; - var max = self.data('easypiechart-max-value') || null; - var adjust = self.data('easypiechart-adjust') || null; + var min = NETDATA.dataAttribute(state.element, 'easypiechart-min-value', null); + var max = NETDATA.dataAttribute(state.element, 'easypiechart-max-value', null); if(min === null) { min = NETDATA.commonMin.get(state); - state.easyPieChartMin = null; + state.tmp.easyPieChartMin = null; } else - state.easyPieChartMin = min; + state.tmp.easyPieChartMin = min; if(max === null) { max = NETDATA.commonMax.get(state); - state.easyPieChartMax = null; + state.tmp.easyPieChartMax = null; } else - state.easyPieChartMax = max; + state.tmp.easyPieChartMax = max; - var pcent = NETDATA.easypiechartPercentFromValueMinMax(value, min, max); + var pcent = NETDATA.easypiechartPercentFromValueMinMax(state, value, min, max); chart.data('data-percent', pcent); - var size; - switch(adjust) { - case 'width': size = state.chartHeight(); break; - case 'min': size = Math.min(state.chartWidth(), state.chartHeight()); break; - case 'max': size = Math.max(state.chartWidth(), state.chartHeight()); break; - case 'height': - default: size = state.chartWidth(); break; - } - state.element.style.width = size + 'px'; - state.element.style.height = size + 'px'; - + var size = state.chartWidth(); var stroke = Math.floor(size / 22); if(stroke < 3) stroke = 2; var valuefontsize = Math.floor((size * 2 / 3) / 5); var valuetop = Math.round((size - valuefontsize - (size / 40)) / 2); - state.easyPieChartLabel = document.createElement('span'); - state.easyPieChartLabel.className = 'easyPieChartLabel'; - state.easyPieChartLabel.innerText = state.legendFormatValue(value); - state.easyPieChartLabel.style.fontSize = valuefontsize + 'px'; - state.easyPieChartLabel.style.top = valuetop.toString() + 'px'; - state.element_chart.appendChild(state.easyPieChartLabel); + state.tmp.easyPieChartLabel = document.createElement('span'); + state.tmp.easyPieChartLabel.className = 'easyPieChartLabel'; + state.tmp.easyPieChartLabel.innerText = state.legendFormatValue(value); + state.tmp.easyPieChartLabel.style.fontSize = valuefontsize + 'px'; + state.tmp.easyPieChartLabel.style.top = valuetop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.easyPieChartLabel); var titlefontsize = Math.round(valuefontsize * 1.6 / 3); var titletop = Math.round(valuetop - (titlefontsize * 2) - (size / 40)); - state.easyPieChartTitle = document.createElement('span'); - state.easyPieChartTitle.className = 'easyPieChartTitle'; - state.easyPieChartTitle.innerText = state.title; - state.easyPieChartTitle.style.fontSize = titlefontsize + 'px'; - state.easyPieChartTitle.style.lineHeight = titlefontsize + 'px'; - state.easyPieChartTitle.style.top = titletop.toString() + 'px'; - state.element_chart.appendChild(state.easyPieChartTitle); + state.tmp.easyPieChartTitle = document.createElement('span'); + state.tmp.easyPieChartTitle.className = 'easyPieChartTitle'; + state.tmp.easyPieChartTitle.innerText = state.title; + state.tmp.easyPieChartTitle.style.fontSize = titlefontsize + 'px'; + state.tmp.easyPieChartTitle.style.lineHeight = titlefontsize + 'px'; + state.tmp.easyPieChartTitle.style.top = titletop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.easyPieChartTitle); var unitfontsize = Math.round(titlefontsize * 0.9); var unittop = Math.round(valuetop + (valuefontsize + unitfontsize) + (size / 40)); - state.easyPieChartUnits = document.createElement('span'); - state.easyPieChartUnits.className = 'easyPieChartUnits'; - state.easyPieChartUnits.innerText = state.units; - state.easyPieChartUnits.style.fontSize = unitfontsize + 'px'; - state.easyPieChartUnits.style.top = unittop.toString() + 'px'; - state.element_chart.appendChild(state.easyPieChartUnits); - - var barColor = self.data('easypiechart-barcolor'); + state.tmp.easyPieChartUnits = document.createElement('span'); + state.tmp.easyPieChartUnits.className = 'easyPieChartUnits'; + state.tmp.easyPieChartUnits.innerText = state.units; + state.tmp.easyPieChartUnits.style.fontSize = unitfontsize + 'px'; + state.tmp.easyPieChartUnits.style.top = unittop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.easyPieChartUnits); + + var barColor = NETDATA.dataAttribute(state.element, 'easypiechart-barcolor', undefined); if(typeof barColor === 'undefined' || barColor === null) - barColor = state.chartColors()[0]; + barColor = state.chartCustomColors()[0]; else { //
    var tmp = eval(barColor); @@ -5504,28 +5756,28 @@ var NETDATA = window.NETDATA || {}; chart.easyPieChart({ barColor: barColor, - trackColor: self.data('easypiechart-trackcolor') || NETDATA.themes.current.easypiechart_track, - scaleColor: self.data('easypiechart-scalecolor') || NETDATA.themes.current.easypiechart_scale, - scaleLength: self.data('easypiechart-scalelength') || 5, - lineCap: self.data('easypiechart-linecap') || 'round', - lineWidth: self.data('easypiechart-linewidth') || stroke, - trackWidth: self.data('easypiechart-trackwidth') || undefined, - size: self.data('easypiechart-size') || size, - rotate: self.data('easypiechart-rotate') || 0, - animate: self.data('easypiechart-animate') || {duration: 500, enabled: true}, - easing: self.data('easypiechart-easing') || undefined + trackColor: NETDATA.dataAttribute(state.element, 'easypiechart-trackcolor', NETDATA.themes.current.easypiechart_track), + scaleColor: NETDATA.dataAttribute(state.element, 'easypiechart-scalecolor', NETDATA.themes.current.easypiechart_scale), + scaleLength: NETDATA.dataAttribute(state.element, 'easypiechart-scalelength', 5), + lineCap: NETDATA.dataAttribute(state.element, 'easypiechart-linecap', 'round'), + lineWidth: NETDATA.dataAttribute(state.element, 'easypiechart-linewidth', stroke), + trackWidth: NETDATA.dataAttribute(state.element, 'easypiechart-trackwidth', undefined), + size: NETDATA.dataAttribute(state.element, 'easypiechart-size', size), + rotate: NETDATA.dataAttribute(state.element, 'easypiechart-rotate', 0), + animate: NETDATA.dataAttribute(state.element, 'easypiechart-animate', {duration: 500, enabled: true}), + easing: NETDATA.dataAttribute(state.element, 'easypiechart-easing', undefined) }); // when we just re-create the chart // do not animate the first update var animate = true; - if(typeof state.easyPieChart_instance !== 'undefined') + if(typeof state.tmp.easyPieChart_instance !== 'undefined') animate = false; - state.easyPieChart_instance = chart.data('easyPieChart'); - if(animate === false) state.easyPieChart_instance.disableAnimation(); - state.easyPieChart_instance.update(pcent); - if(animate === false) state.easyPieChart_instance.enableAnimation(); + state.tmp.easyPieChart_instance = chart.data('easyPieChart'); + if(animate === false) state.tmp.easyPieChart_instance.disableAnimation(); + state.tmp.easyPieChart_instance.update(pcent); + if(animate === false) state.tmp.easyPieChart_instance.enableAnimation(); return true; }; @@ -5568,8 +5820,8 @@ var NETDATA = window.NETDATA || {}; speed = status; // console.log('gauge speed ' + speed); - state.gauge_instance.animationSpeed = speed; - state.___gaugeOld__.speed = speed; + state.tmp.gauge_instance.animationSpeed = speed; + state.tmp.___gaugeOld__.speed = speed; }; NETDATA.gaugeSet = function(state, value, min, max) { @@ -5601,36 +5853,36 @@ var NETDATA = window.NETDATA || {}; if(pcent < 0.001) pcent = 0.001; if(pcent > 99.999) pcent = 99.999; - state.gauge_instance.set(pcent); + state.tmp.gauge_instance.set(pcent); // console.log('gauge set ' + pcent + ', value ' + value + ', min ' + min + ', max ' + max); - state.___gaugeOld__.value = value; - state.___gaugeOld__.min = min; - state.___gaugeOld__.max = max; + state.tmp.___gaugeOld__.value = value; + state.tmp.___gaugeOld__.min = min; + state.tmp.___gaugeOld__.max = max; }; NETDATA.gaugeSetLabels = function(state, value, min, max) { - if(state.___gaugeOld__.valueLabel !== value) { - state.___gaugeOld__.valueLabel = value; - state.gaugeChartLabel.innerText = state.legendFormatValue(value); + if(state.tmp.___gaugeOld__.valueLabel !== value) { + state.tmp.___gaugeOld__.valueLabel = value; + state.tmp.gaugeChartLabel.innerText = state.legendFormatValue(value); } - if(state.___gaugeOld__.minLabel !== min) { - state.___gaugeOld__.minLabel = min; - state.gaugeChartMin.innerText = state.legendFormatValue(min); + if(state.tmp.___gaugeOld__.minLabel !== min) { + state.tmp.___gaugeOld__.minLabel = min; + state.tmp.gaugeChartMin.innerText = state.legendFormatValue(min); } - if(state.___gaugeOld__.maxLabel !== max) { - state.___gaugeOld__.maxLabel = max; - state.gaugeChartMax.innerText = state.legendFormatValue(max); + if(state.tmp.___gaugeOld__.maxLabel !== max) { + state.tmp.___gaugeOld__.maxLabel = max; + state.tmp.gaugeChartMax.innerText = state.legendFormatValue(max); } }; NETDATA.gaugeClearSelection = function(state) { - if(typeof state.gaugeEvent !== 'undefined') { - if(state.gaugeEvent.timer !== undefined) { - clearTimeout(state.gaugeEvent.timer); + if(typeof state.tmp.gaugeEvent !== 'undefined') { + if(state.tmp.gaugeEvent.timer !== undefined) { + clearTimeout(state.tmp.gaugeEvent.timer); } - state.gaugeEvent.timer = undefined; + state.tmp.gaugeEvent.timer = undefined; } if(state.isAutoRefreshable() === true && state.data !== null) { @@ -5654,8 +5906,8 @@ var NETDATA = window.NETDATA || {}; if(slot < 0 || slot >= state.data.result.length) return NETDATA.gaugeClearSelection(state); - if(typeof state.gaugeEvent === 'undefined') { - state.gaugeEvent = { + if(typeof state.tmp.gaugeEvent === 'undefined') { + state.tmp.gaugeEvent = { timer: undefined, value: 0, min: 0, @@ -5664,24 +5916,25 @@ var NETDATA = window.NETDATA || {}; } var value = state.data.result[state.data.result.length - 1 - slot]; - var min = (state.gaugeMin === null)?NETDATA.commonMin.get(state):state.gaugeMin; - var max = (state.gaugeMax === null)?NETDATA.commonMax.get(state):state.gaugeMax; + var min = (state.tmp.gaugeMin === null)?NETDATA.commonMin.get(state):state.tmp.gaugeMin; + var max = (state.tmp.gaugeMax === null)?NETDATA.commonMax.get(state):state.tmp.gaugeMax; // make sure it is zero based - if(min > 0) min = 0; - if(max < 0) max = 0; + // but only if it has not been set by the user + if(state.tmp.gaugeMin === null && min > 0) min = 0; + if(state.tmp.gaugeMax === null && max < 0) max = 0; - state.gaugeEvent.value = value; - state.gaugeEvent.min = min; - state.gaugeEvent.max = max; + state.tmp.gaugeEvent.value = value; + state.tmp.gaugeEvent.min = min; + state.tmp.gaugeEvent.max = max; NETDATA.gaugeSetLabels(state, value, min, max); - if(state.gaugeEvent.timer === undefined) { + if(state.tmp.gaugeEvent.timer === undefined) { NETDATA.gaugeAnimation(state, false); - state.gaugeEvent.timer = setTimeout(function() { - state.gaugeEvent.timer = undefined; - NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max); + state.tmp.gaugeEvent.timer = setTimeout(function() { + state.tmp.gaugeEvent.timer = undefined; + NETDATA.gaugeSet(state, state.tmp.gaugeEvent.value, state.tmp.gaugeEvent.min, state.tmp.gaugeEvent.max); }, NETDATA.options.current.charts_selection_animation_delay); } @@ -5699,14 +5952,15 @@ var NETDATA = window.NETDATA || {}; } else { value = data.result[0]; - min = (state.gaugeMin === null)?NETDATA.commonMin.get(state):state.gaugeMin; - max = (state.gaugeMax === null)?NETDATA.commonMax.get(state):state.gaugeMax; + min = (state.tmp.gaugeMin === null)?NETDATA.commonMin.get(state):state.tmp.gaugeMin; + max = (state.tmp.gaugeMax === null)?NETDATA.commonMax.get(state):state.tmp.gaugeMax; if(value < min) min = value; if(value > max) max = value; // make sure it is zero based - if(min > 0) min = 0; - if(max < 0) max = 0; + // but only if it has not been set by the user + if(state.tmp.gaugeMin === null && min > 0) min = 0; + if(state.tmp.gaugeMax === null && max < 0) max = 0; NETDATA.gaugeSetLabels(state, value, min, max); } @@ -5716,38 +5970,39 @@ var NETDATA = window.NETDATA || {}; }; NETDATA.gaugeChartCreate = function(state, data) { - var self = $(state.element); // var chart = $(state.element_chart); var value = data.result[0]; - var min = self.data('gauge-min-value') || null; - var max = self.data('gauge-max-value') || null; - var adjust = self.data('gauge-adjust') || null; - var pointerColor = self.data('gauge-pointer-color') || NETDATA.themes.current.gauge_pointer; - var strokeColor = self.data('gauge-stroke-color') || NETDATA.themes.current.gauge_stroke; - var startColor = self.data('gauge-start-color') || state.chartColors()[0]; - var stopColor = self.data('gauge-stop-color') || void 0; - var generateGradient = self.data('gauge-generate-gradient') || false; + var min = NETDATA.dataAttribute(state.element, 'gauge-min-value', null); + var max = NETDATA.dataAttribute(state.element, 'gauge-max-value', null); + // var adjust = NETDATA.dataAttribute(state.element, 'gauge-adjust', null); + var pointerColor = NETDATA.dataAttribute(state.element, 'gauge-pointer-color', NETDATA.themes.current.gauge_pointer); + var strokeColor = NETDATA.dataAttribute(state.element, 'gauge-stroke-color', NETDATA.themes.current.gauge_stroke); + var startColor = NETDATA.dataAttribute(state.element, 'gauge-start-color', state.chartCustomColors()[0]); + var stopColor = NETDATA.dataAttribute(state.element, 'gauge-stop-color', void 0); + var generateGradient = NETDATA.dataAttributeBoolean(state.element, 'gauge-generate-gradient', false); if(min === null) { min = NETDATA.commonMin.get(state); - state.gaugeMin = null; + state.tmp.gaugeMin = null; } else - state.gaugeMin = min; + state.tmp.gaugeMin = min; if(max === null) { max = NETDATA.commonMax.get(state); - state.gaugeMax = null; + state.tmp.gaugeMax = null; } else - state.gaugeMax = max; + state.tmp.gaugeMax = max; // make sure it is zero based - if(min > 0) min = 0; - if(max < 0) max = 0; + // but only if it has not been set by the user + if(state.tmp.gaugeMin === null && min > 0) min = 0; + if(state.tmp.gaugeMax === null && max < 0) max = 0; var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5; + // console.log('gauge width: ' + width.toString() + ', height: ' + height.toString()); //switch(adjust) { // case 'width': width = height * ratio; break; // case 'height': @@ -5760,12 +6015,12 @@ var NETDATA = window.NETDATA || {}; var options = { lines: 12, // The number of lines to draw - angle: 0.15, // The span of the gauge arc - lineWidth: 0.50, // The line thickness - radiusScale: 0.85, // Relative radius + angle: 0.14, // The span of the gauge arc + lineWidth: 0.57, // The line thickness + radiusScale: 1.0, // Relative radius pointer: { - length: 0.8, // 0.9 The radius of the inner circle - strokeWidth: 0.035, // The rotation offset + length: 0.85, // 0.9 The radius of the inner circle + strokeWidth: 0.045, // The rotation offset color: pointerColor // Fill color }, limitMax: true, // If false, the max value of the gauge will be updated if value surpass max @@ -5789,7 +6044,7 @@ var NETDATA = window.NETDATA || {}; var len = generateGradient.length; while(len--) { var pcent = generateGradient[len]; - var color = self.attr('data-gauge-gradient-percent-color-' + pcent.toString()) || false; + var color = NETDATA.dataAttribute(state.element, 'gauge-gradient-percent-color-' + pcent.toString(), false); if(color !== false) { var a = []; a[0] = pcent / 100; @@ -5816,57 +6071,57 @@ var NETDATA = window.NETDATA || {}; [1.0, NETDATA.colorLuminance(startColor, 0.0)]]; } - state.gauge_canvas = document.createElement('canvas'); - state.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas'; - state.gauge_canvas.className = 'gaugeChart'; - state.gauge_canvas.width = width; - state.gauge_canvas.height = height; - state.element_chart.appendChild(state.gauge_canvas); + state.tmp.gauge_canvas = document.createElement('canvas'); + state.tmp.gauge_canvas.id = 'gauge-' + state.uuid + '-canvas'; + state.tmp.gauge_canvas.className = 'gaugeChart'; + state.tmp.gauge_canvas.width = width; + state.tmp.gauge_canvas.height = height; + state.element_chart.appendChild(state.tmp.gauge_canvas); - var valuefontsize = Math.floor(height / 6); - var valuetop = Math.round((height - valuefontsize - (height / 6)) / 2); - state.gaugeChartLabel = document.createElement('span'); - state.gaugeChartLabel.className = 'gaugeChartLabel'; - state.gaugeChartLabel.style.fontSize = valuefontsize + 'px'; - state.gaugeChartLabel.style.top = valuetop.toString() + 'px'; - state.element_chart.appendChild(state.gaugeChartLabel); + var valuefontsize = Math.floor(height / 5); + var valuetop = Math.round((height - valuefontsize) / 3.2); + state.tmp.gaugeChartLabel = document.createElement('span'); + state.tmp.gaugeChartLabel.className = 'gaugeChartLabel'; + state.tmp.gaugeChartLabel.style.fontSize = valuefontsize + 'px'; + state.tmp.gaugeChartLabel.style.top = valuetop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartLabel); - var titlefontsize = Math.round(valuefontsize / 2); + var titlefontsize = Math.round(valuefontsize / 2.1); var titletop = 0; - state.gaugeChartTitle = document.createElement('span'); - state.gaugeChartTitle.className = 'gaugeChartTitle'; - state.gaugeChartTitle.innerText = state.title; - state.gaugeChartTitle.style.fontSize = titlefontsize + 'px'; - state.gaugeChartTitle.style.lineHeight = titlefontsize + 'px'; - state.gaugeChartTitle.style.top = titletop.toString() + 'px'; - state.element_chart.appendChild(state.gaugeChartTitle); + state.tmp.gaugeChartTitle = document.createElement('span'); + state.tmp.gaugeChartTitle.className = 'gaugeChartTitle'; + state.tmp.gaugeChartTitle.innerText = state.title; + state.tmp.gaugeChartTitle.style.fontSize = titlefontsize + 'px'; + state.tmp.gaugeChartTitle.style.lineHeight = titlefontsize + 'px'; + state.tmp.gaugeChartTitle.style.top = titletop.toString() + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartTitle); var unitfontsize = Math.round(titlefontsize * 0.9); - state.gaugeChartUnits = document.createElement('span'); - state.gaugeChartUnits.className = 'gaugeChartUnits'; - state.gaugeChartUnits.innerText = state.units; - state.gaugeChartUnits.style.fontSize = unitfontsize + 'px'; - state.element_chart.appendChild(state.gaugeChartUnits); - - state.gaugeChartMin = document.createElement('span'); - state.gaugeChartMin.className = 'gaugeChartMin'; - state.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px'; - state.element_chart.appendChild(state.gaugeChartMin); - - state.gaugeChartMax = document.createElement('span'); - state.gaugeChartMax.className = 'gaugeChartMax'; - state.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px'; - state.element_chart.appendChild(state.gaugeChartMax); + state.tmp.gaugeChartUnits = document.createElement('span'); + state.tmp.gaugeChartUnits.className = 'gaugeChartUnits'; + state.tmp.gaugeChartUnits.innerText = state.units; + state.tmp.gaugeChartUnits.style.fontSize = unitfontsize + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartUnits); + + state.tmp.gaugeChartMin = document.createElement('span'); + state.tmp.gaugeChartMin.className = 'gaugeChartMin'; + state.tmp.gaugeChartMin.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartMin); + + state.tmp.gaugeChartMax = document.createElement('span'); + state.tmp.gaugeChartMax.className = 'gaugeChartMax'; + state.tmp.gaugeChartMax.style.fontSize = Math.round(valuefontsize * 0.75).toString() + 'px'; + state.element_chart.appendChild(state.tmp.gaugeChartMax); // when we just re-create the chart // do not animate the first update var animate = true; - if(typeof state.gauge_instance !== 'undefined') + if(typeof state.tmp.gauge_instance !== 'undefined') animate = false; - state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge! + state.tmp.gauge_instance = new Gauge(state.tmp.gauge_canvas).setOptions(options); // create sexy gauge! - state.___gaugeOld__ = { + state.tmp.___gaugeOld__ = { value: value, min: min, max: max, @@ -5876,8 +6131,8 @@ var NETDATA = window.NETDATA || {}; }; // we will always feed a percentage - state.gauge_instance.minValue = 0; - state.gauge_instance.maxValue = 100; + state.tmp.gauge_instance.minValue = 0; + state.tmp.gauge_instance.maxValue = 100; NETDATA.gaugeAnimation(state, animate); NETDATA.gaugeSet(state, value, min, max); @@ -5895,8 +6150,8 @@ var NETDATA = window.NETDATA || {}; create: NETDATA.dygraphChartCreate, update: NETDATA.dygraphChartUpdate, resize: function(state) { - if(typeof state.dygraph_instance.resize === 'function') - state.dygraph_instance.resize(); + if(typeof state.tmp.dygraph_instance.resize === 'function') + state.tmp.dygraph_instance.resize(); }, setSelection: NETDATA.dygraphSetSelection, clearSelection: NETDATA.dygraphClearSelection, @@ -5915,11 +6170,16 @@ var NETDATA = window.NETDATA || {}; return (this.isSparkline(state) === false)?3:2; }, isSparkline: function(state) { - if(typeof state.dygraph_sparkline === 'undefined') { - var t = $(state.element).data('dygraph-theme'); - state.dygraph_sparkline = (t === 'sparkline'); + if(typeof state.tmp.dygraph_sparkline === 'undefined') { + var t = NETDATA.dataAttribute(state.element, 'dygraph-theme', undefined); + state.tmp.dygraph_sparkline = (t === 'sparkline'); } - return state.dygraph_sparkline; + return state.tmp.dygraph_sparkline; + }, + container_class: function(state) { + if(this.legend(state) !== null) + return 'netdata-container-with-legend'; + return 'netdata-container'; } }, "sparkline": { @@ -5938,7 +6198,8 @@ var NETDATA = window.NETDATA || {}; autoresize: function(state) { void(state); return false; }, max_updates_to_recreate: function(state) { void(state); return 5000; }, track_colors: function(state) { void(state); return false; }, - pixels_per_point: function(state) { void(state); return 3; } + pixels_per_point: function(state) { void(state); return 3; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "peity": { initialize: NETDATA.peityInitialize, @@ -5956,7 +6217,8 @@ var NETDATA = window.NETDATA || {}; autoresize: function(state) { void(state); return false; }, max_updates_to_recreate: function(state) { void(state); return 5000; }, track_colors: function(state) { void(state); return false; }, - pixels_per_point: function(state) { void(state); return 3; } + pixels_per_point: function(state) { void(state); return 3; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "morris": { initialize: NETDATA.morrisInitialize, @@ -5974,7 +6236,8 @@ var NETDATA = window.NETDATA || {}; autoresize: function(state) { void(state); return false; }, max_updates_to_recreate: function(state) { void(state); return 50; }, track_colors: function(state) { void(state); return false; }, - pixels_per_point: function(state) { void(state); return 15; } + pixels_per_point: function(state) { void(state); return 15; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "google": { initialize: NETDATA.googleInitialize, @@ -5992,7 +6255,8 @@ var NETDATA = window.NETDATA || {}; autoresize: function(state) { void(state); return false; }, max_updates_to_recreate: function(state) { void(state); return 300; }, track_colors: function(state) { void(state); return false; }, - pixels_per_point: function(state) { void(state); return 4; } + pixels_per_point: function(state) { void(state); return 4; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "raphael": { initialize: NETDATA.raphaelInitialize, @@ -6010,7 +6274,8 @@ var NETDATA = window.NETDATA || {}; autoresize: function(state) { void(state); return false; }, max_updates_to_recreate: function(state) { void(state); return 5000; }, track_colors: function(state) { void(state); return false; }, - pixels_per_point: function(state) { void(state); return 3; } + pixels_per_point: function(state) { void(state); return 3; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "c3": { initialize: NETDATA.c3Initialize, @@ -6028,7 +6293,8 @@ var NETDATA = window.NETDATA || {}; autoresize: function(state) { void(state); return false; }, max_updates_to_recreate: function(state) { void(state); return 5000; }, track_colors: function(state) { void(state); return false; }, - pixels_per_point: function(state) { void(state); return 15; } + pixels_per_point: function(state) { void(state); return 15; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "d3": { initialize: NETDATA.d3Initialize, @@ -6046,7 +6312,8 @@ var NETDATA = window.NETDATA || {}; autoresize: function(state) { void(state); return false; }, max_updates_to_recreate: function(state) { void(state); return 5000; }, track_colors: function(state) { void(state); return false; }, - pixels_per_point: function(state) { void(state); return 3; } + pixels_per_point: function(state) { void(state); return 3; }, + container_class: function(state) { void(state); return 'netdata-container'; } }, "easypiechart": { initialize: NETDATA.easypiechartInitialize, @@ -6065,7 +6332,8 @@ var NETDATA = window.NETDATA || {}; max_updates_to_recreate: function(state) { void(state); return 5000; }, track_colors: function(state) { void(state); return true; }, pixels_per_point: function(state) { void(state); return 3; }, - aspect_ratio: 100 + aspect_ratio: 100, + container_class: function(state) { void(state); return 'netdata-container-easypiechart'; } }, "gauge": { initialize: NETDATA.gaugeInitialize, @@ -6084,7 +6352,8 @@ var NETDATA = window.NETDATA || {}; max_updates_to_recreate: function(state) { void(state); return 5000; }, track_colors: function(state) { void(state); return true; }, pixels_per_point: function(state) { void(state); return 3; }, - aspect_ratio: 70 + aspect_ratio: 60, + container_class: function(state) { void(state); return 'netdata-container-gauge'; } } }; @@ -6203,7 +6472,7 @@ var NETDATA = window.NETDATA || {}; NETDATA.alarms = { onclick: null, // the callback to handle the click - it will be called with the alarm log entry - chart_div_offset: 100, // give that space above the chart when scrolling to it + chart_div_offset: -50, // give that space above the chart when scrolling to it chart_div_id_prefix: 'chart_', // the chart DIV IDs have this prefix (they should be NETDATA.name2id(chart.id)) chart_div_animation_duration: 0,// the duration of the animation while scrolling to a chart @@ -6349,7 +6618,7 @@ var NETDATA = window.NETDATA || {}; if(typeof chart_id === 'string') { var offset = $('#' + NETDATA.alarms.chart_div_id_prefix + NETDATA.name2id(chart_id)).offset(); if(typeof offset !== 'undefined') { - $('html, body').animate({ scrollTop: offset.top - NETDATA.alarms.chart_div_offset }, NETDATA.alarms.chart_div_animation_duration); + $('html, body').animate({ scrollTop: offset.top + NETDATA.alarms.chart_div_offset }, NETDATA.alarms.chart_div_animation_duration); return true; } } diff --git a/web/dashboard.slate.css b/web/dashboard.slate.css index 36ea6dc6a..f12a6aab9 100644 --- a/web/dashboard.slate.css +++ b/web/dashboard.slate.css @@ -63,6 +63,38 @@ code { /* width and height is given per chart with data-width and data-height */ } +.netdata-container-gauge { + display: inline-block; + overflow: hidden; + + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ +} + +.netdata-container-gauge:after { + padding-top: 60%; + display: block; + content: ''; +} + +.netdata-container-easypiechart { + display: inline-block; + overflow: hidden; + + /* required for child elements to have absolute position */ + position: relative; + + /* width and height is given per chart with data-width and data-height */ +} + +.netdata-container-easypiechart:after { + padding-top: 100%; + display: block; + content: ''; +} + .netdata-aspect { position: relative; width: 100%; @@ -142,12 +174,15 @@ code { .netdata-message { display: inline-block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; text-align: left; vertical-align: top; font-weight: bold; font-size: x-small; - width: 100%; - height: 100%; overflow: hidden; background: inherit; z-index: 0; @@ -417,7 +452,7 @@ code { margin-left: 18%; text-align: center; color: #676b70; - font-weight: normal; + font-weight: bold; } .easyPieChartUnits { @@ -441,6 +476,8 @@ code { position: absolute; top: 0; left: 0; + bottom: 0; + right: 0; z-index: 0; } @@ -489,7 +526,7 @@ code { position: absolute; float: left; left: 0; - bottom: 10%; + bottom: 8%; width: 92%; margin-left: 8%; text-align: left; @@ -502,7 +539,7 @@ code { position: absolute; float: left; left: 0; - bottom: 10%; + bottom: 8%; width: 95%; margin-right: 5%; text-align: right; diff --git a/web/dashboard_info.js b/web/dashboard_info.js index c348da30d..91e007a1d 100644 --- a/web/dashboard_info.js +++ b/web/dashboard_info.js @@ -61,6 +61,12 @@ netdataDashboard.menu = { info: 'Performance metrics of the netfilter components.' }, + 'ipfw': { + title: 'Firewall (ipfw)', + icon: '', + info: 'Counters and memory usage for the ipfw rules.' + }, + 'cpu': { title: 'CPUs', icon: '', @@ -91,6 +97,12 @@ netdataDashboard.menu = { info: 'The Intelligent Platform Management Interface (IPMI) is a set of computer interface specifications for an autonomous computer subsystem that provides management and monitoring capabilities independently of the host system\'s CPU, firmware (BIOS or UEFI) and operating system.' }, + 'samba': { + title: 'Samba', + icon: "", + info: 'Performance metrics of the Samba file share operations of this system. Samba is a implementation of Windows services, including Windows SMB protocol file shares.' + }, + 'nfsd': { title: 'NFS Server', icon: '', @@ -103,6 +115,12 @@ netdataDashboard.menu = { info: 'Performance metrics of the NFS operations of this system, acting as an NFS client.' }, + 'zfs': { + title: 'ZFS filesystem', + icon: '', + info: 'Performance metrics of the ZFS filesystem. The following charts visualize all metrics reported by arcstat.py and arc_summary.py.' + }, + 'apps': { title: 'Applications', icon: '', @@ -225,10 +243,16 @@ netdataDashboard.menu = { info: undefined }, + 'lighttpd': { + title: 'Lighttpd', + icon: '', + info: undefined + }, + 'web_log': { title: undefined, icon: '', - info: 'Information extracted from a web server log file. web_log plugin incrementally parses the web server log file to provide, in real-time, a break down of key web server performance metrics. An extended log file format may optionally be used (for nginx and apache) offering timing information and bandwidth for both requests and responses. web_log plugin may also be configured to provide a break down of requests per URL pattern (check /etc/netdata/python.d/web_log.conf).' + info: 'Information extracted from a server log file. web_log plugin incrementally parses the server log file to provide, in real-time, a break down of key server performance metrics. For web servers, an extended log file format may optionally be used (for nginx and apache) offering timing information and bandwidth for both requests and responses. web_log plugin may also be configured to provide a break down of requests per URL pattern (check /etc/netdata/python.d/web_log.conf).' }, 'named': { @@ -261,10 +285,22 @@ netdataDashboard.menu = { info: undefined }, + 'fronius': { + title: 'Fronius', + icon: '', + info: undefined + }, + 'snmp': { title: 'SNMP', icon: '', info: undefined + }, + + 'go_expvar': { + title: 'Go - expvars', + icon: '', + info: 'Statistics about running Go applications exposed by the expvar package.' } }; @@ -277,6 +313,44 @@ netdataDashboard.menu = { // information about the submenus netdataDashboard.submenu = { + 'web_log.squid_bandwidth': { + title: 'bandwidth', + info: 'Bandwidth of responses (sent) by squid. This chart may present unusual spikes, since the bandwidth is accounted at the time the log line is saved by the server, even if the time needed to serve it spans across a longer duration. We suggest to use QoS (e.g. FireQOS) for accurate accounting of the server bandwidth.' + }, + + 'web_log.squid_responses': { + title: 'responses', + info: 'Information related to the responses sent by squid.' + }, + + 'web_log.squid_requests': { + title: 'requests', + info: 'Information related to the requests squid has received.' + }, + + 'web_log.squid_hierarchy': { + title: 'hierarchy', + info: 'Performance metrics for the squid hierarchy used to serve the requests.' + }, + + 'web_log.squid_squid_transport': { + title: 'transport' + }, + + 'web_log.squid_squid_cache': { + title: 'cache', + info: 'Performance metrics for the performance of the squid cache.' + }, + + 'web_log.squid_timings': { + title: 'timings', + info: 'Duration of squid requests. Unrealistic spikes may be reported, since squid logs the total time of the requests, when they complete. Especially for HTTPS, the clients get a tunnel from the proxy and exchange requests directly with the upstream servers, so squid cannot evaluate the individual requests and reports the total time the tunnel was open.' + }, + + 'web_log.squid_clients': { + title: 'clients' + }, + 'web_log.bandwidth': { info: 'Bandwidth of requests (received) and responses (sent). received requires an extended log format (without it, the web server log does not have this information). This chart may present unusual spikes, since the bandwidth is accounted at the time the log line is saved by the web server, even if the time needed to serve it spans across a longer duration. We suggest to use QoS (e.g. FireQOS) for accurate accounting of the web server bandwidth.' }, @@ -321,6 +395,11 @@ netdataDashboard.submenu = { info: 'DDoS protection performance metrics. SYNPROXY is a TCP SYN packets proxy. It is used to protect any TCP server (like a web server) from SYN floods and similar DDoS attacks. It is a netfilter module, in the Linux kernel (since version 3.12). It is optimized to handle millions of packets per second utilizing all CPUs available without any concurrency locking between the connections. It can be used for any kind of TCP traffic (even encrypted), since it does not interfere with the content itself.' }, + 'ipfw.dynamic_rules': { + title: 'dynamic rules', + info: 'Number of dynamic rules, created by correspondent stateful firewall rules.' + }, + 'system.softnet_stat': { title: 'softnet', info: function(os) { @@ -339,6 +418,11 @@ netdataDashboard.submenu = { else return 'Statistics for per CPUs core SoftIRQs related to network receive work. Total for all CPU cores can be found at System / softnet statistics.'; } + }, + + 'go_expvar.memstats': { + title: 'Memory statistics', + info: 'Go runtime memory statistics. See runtime.MemStats documentation for more info about each chart and the values.' } }; @@ -420,7 +504,6 @@ netdataDashboard.context = { }, 'system.idlejitter': { - colors: '#5555AA', info: 'Idle jitter is calculated by netdata. A thread is spawned that requests to sleep for a few microseconds. When the system wakes it up, it measures how many microseconds have passed. The difference between the requested and the actual duration of the sleep, is the idle jitter. This number is useful in real-time environments, where CPU jitter can affect the quality of the service (like VoIP media gateways).' }, @@ -440,6 +523,30 @@ netdataDashboard.context = { info: 'System swap memory usage. Swap space is used when the amount of physical memory (RAM) is full. When the system needs more memory resources and the RAM is full, inactive pages in memory are moved to the swap space (usually a disk, a disk partition or a file).' }, + // ------------------------------------------------------------------------ + // CPU charts + + 'cpu.cpu': { + commonMin: true, + commonMax: true, + valueRange: "[0, 100]" + }, + + 'cpu.interrupts': { + commonMin: true, + commonMax: true + }, + + 'cpu.softirqs': { + commonMin: true, + commonMax: true + }, + + 'cpu.softnet_stat': { + commonMin: true, + commonMax: true + }, + // ------------------------------------------------------------------------ // MEMORY @@ -782,6 +889,66 @@ netdataDashboard.context = { }, + // ------------------------------------------------------------------------ + // LIGHTTPD + + 'lighttpd.connections': { + colors: NETDATA.colors[4], + mainheads: [ + netdataDashboard.gaugeChart('Connections', '12%', '', NETDATA.colors[4]) + ] + }, + + 'lighttpd.requests': { + colors: NETDATA.colors[0], + mainheads: [ + netdataDashboard.gaugeChart('Requests', '12%', '', NETDATA.colors[0]) + ] + }, + + 'lighttpd.net': { + colors: NETDATA.colors[3], + mainheads: [ + netdataDashboard.gaugeChart('Bandwidth', '12%', '', NETDATA.colors[3]) + ] + }, + + 'lighttpd.workers': { + mainheads: [ + function(os, id) { + void(os); + return '
    '; + } + ] + }, + + 'lighttpd.bytesperreq': { + colors: NETDATA.colors[3], + height: 0.5 + }, + + 'lighttpd.reqpersec': { + colors: NETDATA.colors[4], + height: 0.5 + }, + + 'lighttpd.bytespersec': { + colors: NETDATA.colors[6], + height: 0.5 + }, + // ------------------------------------------------------------------------ // NGINX @@ -853,6 +1020,9 @@ netdataDashboard.context = { height: 0.5 }, + // ------------------------------------------------------------------------ + // web_log + 'web_log.response_statuses': { info: 'Web server responses by type. success includes 1xx, 2xx and 304, error includes 5xx, redirect includes 3xx except 304, bad includes 4xx, other are all the other responses.', mainheads: [ @@ -931,7 +1101,14 @@ netdataDashboard.context = { }, 'web_log.response_codes': { - info: 'Web server responses by code family. According to the standards 1xx are informational responses, 2xx are successful responses, 3xx are redirects (although they include 304 which is used as "not modified"), 4xx are bad requests, 5xx are internal server errors, other are non-standard responses, unmatched counts the lines in the log file that are not matched by the plugin (let us know if you have any unmatched).' + info: 'Web server responses by code family. ' + + 'According to the standards 1xx are informational responses, ' + + '2xx are successful responses, ' + + '3xx are redirects (although they include 304 which is used as "not modified"), ' + + '4xx are bad requests, ' + + '5xx are internal server errors, ' + + 'other are non-standard responses, ' + + 'unmatched counts the lines in the log file that are not matched by the plugin (let us know if you have any unmatched).' }, 'web_log.response_time': { @@ -969,6 +1146,212 @@ netdataDashboard.context = { 'web_log.clients_all': { info: 'Unique client IPs accessing the web server since the last restart of netdata. This plugin keeps in memory all the unique IPs that have accessed the web server. On very busy web servers (several millions of unique IPs) you may want to disable this chart (check /etc/netdata/python.d/web_log.conf).' - } + }, + + // ------------------------------------------------------------------------ + // web_log for squid + + 'web_log.squid_response_statuses': { + info: 'Squid responses by type. ' + + 'success includes 1xx, 2xx, 000, 304, ' + + 'error includes 5xx and 6xx, ' + + 'redirect includes 3xx except 304, ' + + 'bad includes 4xx, ' + + 'other are all the other responses.', + mainheads: [ + function(os, id) { + void(os); + return '
    '; + }, + + function(os, id) { + void(os); + return '
    '; + }, + function(os, id) { + void(os); + return '
    '; + }, + + function(os, id) { + void(os); + return '
    '; + } + ] + }, + + 'web_log.squid_response_codes': { + info: 'Web server responses by code family. ' + + 'According to HTTP standards 1xx are informational responses, ' + + '2xx are successful responses, ' + + '3xx are redirects (although they include 304 which is used as "not modified"), ' + + '4xx are bad requests, ' + + '5xx are internal server errors. ' + + 'Squid also defines 000 mostly for UDP requests, and ' + + '6xx for broken upstream servers sending wrong headers. ' + + 'Finally, other are non-standard responses, and ' + + 'unmatched counts the lines in the log file that are not matched by the plugin (let us know if you have any unmatched).' + }, + + 'web_log.squid_duration': { + mainheads: [ + function(os, id) { + void(os); + return '
    '; + } + ] + }, + + 'web_log.squid_detailed_response_codes': { + info: 'Number of responses for each response code individually.' + }, + + 'web_log.squid_clients': { + info: 'Unique client IPs accessing squid, within each data collection iteration. If data collection is per second, this chart shows unique client IPs per second.' + }, + + 'web_log.squid_clients_all': { + info: 'Unique client IPs accessing squid since the last restart of netdata. This plugin keeps in memory all the unique IPs that have accessed the server. On very busy squid servers (several millions of unique IPs) you may want to disable this chart (check /etc/netdata/python.d/web_log.conf).' + }, + + 'web_log.squid_transport_methods': { + info: 'Break down per delivery method: TCP are requests on the HTTP port (usually 3128), ' + + 'UDP are requests on the ICP port (usually 3130), or HTCP port (usually 4128). ' + + 'If ICP logging was disabled using the log_icp_queries option, no ICP replies will be logged. ' + + 'NONE are used to state that squid delivered an unusual response or no response at all. ' + + 'Seen with cachemgr requests and errors, usually when the transaction fails before being classified into one of the above outcomes. ' + + 'Also seen with responses to CONNECT requests.' + }, + + 'web_log.squid_code': { + info: 'These are combined squid result status codes. A break down per component is given in the following charts. ' + + 'Check the squid documentation about them.' + }, + + 'web_log.squid_handling_opts': { + info: 'These tags are optional and describe why the particular handling was performed or where the request came from. ' + + 'CLIENT means that the client request placed limits affecting the response. Usually seen with client issued a no-cache, or analogous cache control command along with the request. Thus, the cache has to validate the object.' + + 'IMS states that the client sent a revalidation (conditional) request. ' + + 'ASYNC, is used when the request was generated internally by Squid. Usually this is background fetches for cache information exchanges, background revalidation from stale-while-revalidate cache controls, or ESI sub-objects being loaded. ' + + 'SWAPFAIL is assigned when the object was believed to be in the cache, but could not be accessed. A new copy was requested from the server. ' + + 'REFRESH when a revalidation (conditional) request was sent to the server. ' + + 'SHARED when this request was combined with an existing transaction by collapsed forwarding. NOTE: the existing request is not marked as SHARED. ' + + 'REPLY when particular handling was requested in the HTTP reply from server or peer. Usually seen on DENIED due to http_reply_access ACLs preventing delivery of servers response object to the client.' + }, + + 'web_log.squid_object_types': { + info: 'These tags are optional and describe what type of object was produced. ' + + 'NEGATIVE is only seen on HIT responses, indicating the response was a cached error response. e.g. 404 not found. ' + + 'STALE means the object was cached and served stale. This is usually caused by stale-while-revalidate or stale-if-error cache controls. ' + + 'OFFLINE when the requested object was retrieved from the cache during offline_mode. The offline mode never validates any object. ' + + 'INVALID when an invalid request was received. An error response was delivered indicating what the problem was. ' + + 'FAIL is only seen on REFRESH to indicate the revalidation request failed. The response object may be the server provided network error or the stale object which was being revalidated depending on stale-if-error cache control. ' + + 'MODIFIED is only seen on REFRESH responses to indicate revalidation produced a new modified object. ' + + 'UNMODIFIED is only seen on REFRESH responses to indicate revalidation produced a 304 (Not Modified) status, which was relayed to the client. ' + + 'REDIRECT when squid generated an HTTP redirect response to this request.' + }, + + 'web_log.squid_cache_events': { + info: 'These tags are optional and describe whether the response was loaded from cache, network, or otherwise. ' + + 'HIT when the response object delivered was the local cache object. ' + + 'MEM when the response object came from memory cache, avoiding disk accesses. Only seen on HIT responses. ' + + 'MISS when the response object delivered was the network response object. ' + + 'DENIED when the request was denied by access controls. ' + + 'NOFETCH an ICP specific type, indicating service is alive, but not to be used for this request (sent during "-Y" startup, or during frequent failures, a cache in hit only mode will return either UDP_HIT or UDP_MISS_NOFETCH. Neighbours will thus only fetch hits). ' + + 'TUNNEL when a binary tunnel was established for this transaction.' + }, + + 'web_log.squid_transport_errors': { + info: 'These tags are optional and describe some error conditions which occured during response delivery (if any). ' + + 'ABORTED when the response was not completed due to the connection being aborted (usually by the client). ' + + 'TIMEOUT, when the response was not completed due to a connection timeout.' + }, + + // ------------------------------------------------------------------------ + // Fronius Solar Power + + 'fronius.power': { + info: 'Positive Grid values mean that power is coming from the grid. Negative values are excess power that is going back into the grid, possibly selling it. ' + + 'Photovoltaics is the power generated from the solar panels. ' + + 'Accumulator is the stored power in the accumulator, if one is present.' + }, + + 'fronius.autonomy': { + commonMin: true, + commonMax: true, + valueRange: "[0, 100]", + info: 'The Autonomy is the percentage of how autonomous the installation is. An autonomy of 100 % means that the installation is producing more energy than it is needed. ' + + 'The Self consumption indicates the ratio between the current power generated and the current load. When it reaches 100 %, the Autonomy declines, since the solar panels can not produce enough energy and need support from the grid.' + }, + + 'fronius.energy.today': { + commonMin: true, + commonMax: true, + valueRange: "[0, null]" + } }; diff --git a/web/index.html b/web/index.html index 250dbfed3..be944e34d 100644 --- a/web/index.html +++ b/web/index.html @@ -591,6 +591,43 @@ urlOptions.help = loadLocalStorage('options.show_help'); } + // -------------------------------------------------------------------- + // natural sorting + // http://www.davekoelle.com/files/alphanum.js - LGPL + + function naturalSortChunkify(t) { + var tz = []; + var x = 0, y = -1, n = 0, i, j; + + while (i = (j = t.charAt(x++)).charCodeAt(0)) { + var m = (i === 46 || (i >= 48 && i <= 57)); + if (m !== n) { + tz[++y] = ""; + n = m; + } + tz[y] += j; + } + + return tz; + } + + function naturalSortCompare(a, b) { + var aa = naturalSortChunkify(a.toLowerCase()); + var bb = naturalSortChunkify(b.toLowerCase()); + + for (var x = 0; aa[x] && bb[x]; x++) { + if (aa[x] !== bb[x]) { + var c = Number(aa[x]), d = Number(bb[x]); + if (c.toString() === aa[x] && d.toString() === bb[x]) + return c - d; + else + return (aa[x] > bb[x]) ? 1 : -1; + } + } + + return aa.length - bb.length; + } + // -------------------------------------------------------------------- // registry call back to render my-netdata menu @@ -616,16 +653,14 @@ var master = options.hosts[0].hostname; var sorted = options.hosts.sort(function(a, b) { if(a.hostname === master) return -1; - if(a.hostname === b.hostname) return 0; - else if(a.hostname > b.hostname) return 1; - return -1; + return naturalSortCompare(a.hostname, b.hostname); }); i = 0; len = sorted.length; while(len--) { hostname = sorted[i].hostname; - if(hostname == master) { + if(hostname === master) { url = base + "/"; icon = "home"; } @@ -656,14 +691,13 @@ saveLocalStorage("registryCallback", JSON.stringify(machines_array)); var machines = machines_array.sort(function (a, b) { - if (a.name > b.name) return -1; - if (a.name < b.name) return 1; - return 0; + return naturalSortCompare(a.name, b.name); }); + i = 0; len = machines.length; while(len--) { - var u = machines[len]; + var u = machines[i++]; found++; el += '
  • ' + u.name + '
  • '; a1 += '
  • '; @@ -936,6 +970,8 @@ }; function chartsPerRow(total) { + void(total); + if(options.chartsPerRow === 0) { return 1; //var width = Math.floor(total / options.chartsMinWidth); @@ -948,8 +984,7 @@ function prioritySort(a, b) { if(a.priority < b.priority) return -1; if(a.priority > b.priority) return 1; - if(a.name < b.name) return -1; - return 1; + return naturalSortCompare(a.name, b.name); } function sortObjectByPriority(object) { @@ -968,8 +1003,7 @@ sorted.sort(function(a, b) { if(idx[a].priority < idx[b].priority) return -1; if(idx[a].priority > idx[b].priority) return 1; - if(a < b) return -1; - return 1; + return naturalSortCompare(a, b); }); return sorted; @@ -980,10 +1014,10 @@ // scroll to a section, without changing the browser history function scrollToId(hash) { - if(hash && hash != '' && document.getElementById(hash) !== null) { + if(hash && hash !== '' && document.getElementById(hash) !== null) { var offset = $('#' + hash).offset(); if(typeof offset !== 'undefined') - $('html, body').animate({ scrollTop: offset.top }, 0); + $('html, body').animate({ scrollTop: offset.top - 30 }, 0); } // we must return false to prevent the default action @@ -1035,7 +1069,7 @@ key = key + '.' + this.sparklines_registry[key].count; - return prefix + '
    (X' + units + ')' + suffix; + return prefix + '
    (X' + units + ')' + suffix; }, gaugeChart: function(title, width, dimensions, colors) { @@ -1045,7 +1079,7 @@ if(typeof dimensions === 'undefined') dimensions = ''; - return '
    2 && parts[1] === 'expvar') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + case 'isc': chart.menu = chart.type; if(parts.length > 2 && parts[1] === 'dhcpd') @@ -1260,14 +1303,14 @@ var head = ''; if(typeof charts['system.swap'] !== 'undefined') - head += '
    '; if(typeof charts['system.io'] !== 'undefined') { - head += '
    '; - head += '
    '; if(typeof charts['system.ipv4'] !== 'undefined') { - head += '
    '; - head += '
    '; } else if(typeof charts['system.ipv6'] !== 'undefined') { - head += '
    '; - head += '
    '; // console.log(' \------- ' + chart.id + ' (' + chart.priority + '): ' + chart.context + ' height: ' + menus[menu].submenus[submenu].height); @@ -1471,7 +1532,7 @@ sidebar += '
  • add more charts
  • '; sidebar += '
  • add more alarms
  • '; - sidebar += '
  • netdata on ' + data.hostname.toString() + ', collects every ' + ((data.update_every == 1)?'second':data.update_every.toString() + ' seconds') + ' ' + data.dimensions_count.toLocaleString() + ' metrics, presented as ' + data.charts_count.toLocaleString() + ' charts and monitored by ' + data.alarms_count.toLocaleString() + ' alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + seconds4human(data.update_every * data.history) + ' of real-time history.
     
    netdata
    v' + data.version.toString() +'
  • '; + sidebar += '
  • netdata on ' + data.hostname.toString() + ', collects every ' + ((data.update_every === 1)?'second':data.update_every.toString() + ' seconds') + ' ' + data.dimensions_count.toLocaleString() + ' metrics, presented as ' + data.charts_count.toLocaleString() + ' charts and monitored by ' + data.alarms_count.toLocaleString() + ' alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + seconds4human(data.update_every * data.history) + ' of real-time history.
     
    netdata
    v' + data.version.toString() +'
  • '; sidebar += ''; div.innerHTML = html; document.getElementById('sidebar').innerHTML = sidebar; @@ -1619,7 +1680,7 @@ var t = new Date(timestamp * 1000); var now = new Date(); - if(t.toDateString() == now.toDateString()) + if(t.toDateString() === now.toDateString()) return t.toLocaleTimeString(); return t.toLocaleDateString() + space + t.toLocaleTimeString(); @@ -1676,8 +1737,8 @@ } var delay = ''; - if((alarm.delay_up_duration > 0 || alarm.delay_down_duration > 0) && alarm.delay_multiplier != 0 && alarm.delay_max_duration > 0) { - if(alarm.delay_up_duration == alarm.delay_down_duration) { + if((alarm.delay_up_duration > 0 || alarm.delay_down_duration > 0) && alarm.delay_multiplier !== 0 && alarm.delay_max_duration > 0) { + if(alarm.delay_up_duration === alarm.delay_down_duration) { delay += '
    hysteresis ' + seconds4human(alarm.delay_up_duration, { negative_suffix: '' }); } else { @@ -1689,7 +1750,7 @@ delay += 'on recovery ' + seconds4human(alarm.delay_down_duration, { negative_suffix: '' }) + ', '; } } - if(alarm.delay_multiplier != 1.0) { + if(alarm.delay_multiplier !== 1.0) { delay += 'multiplied by ' + alarm.delay_multiplier.toString() + ''; delay += ', up to ' + seconds4human(alarm.delay_max_duration, { negative_suffix: '' }) + ''; } @@ -1762,15 +1823,16 @@ // sort the families, like the dashboard menu does var families_sorted = families_sort.sort(function (a, b) { - if (a.priority > b.priority) return -1; - if (a.priority < b.priority) return 1; - return 0; + if (a.priority < b.priority) return -1; + if (a.priority > b.priority) return 1; + return naturalSortCompare(a.name, b.name); }); + var i = 0; var fc = 0; var len = families_sorted.length; while(len--) { - family = families_sorted[len].name; + family = families_sorted[i++].name; var active_family_added = false; var expanded = 'true'; var collapsed = ''; @@ -2253,7 +2315,7 @@ if(hours > 1) txt += hours.toString() + options.space + options.hours; else if(hours === 1) txt += hours.toString() + options.space + options.hour; - if(hours > 0 && minutes > 0 && seconds == 0) + if(hours > 0 && minutes > 0 && seconds === 0) txt += options.space + options.and + options.space; else if(hours > 0 && minutes > 0 && seconds > 0) txt += ',' + options.space; @@ -2293,7 +2355,7 @@ options.version = data.version; netdataDashboard.os = data.os; - if(typeof data.hosts != 'undefined') + if(typeof data.hosts !=='undefined') options.hosts = data.hosts; // update the dashboard hostname @@ -2328,7 +2390,7 @@ netdata_url = NETDATA.serverDefault; // initialize clickable alarms - NETDATA.alarms.chart_div_offset = 100; + NETDATA.alarms.chart_div_offset = -50; NETDATA.alarms.chart_div_id_prefix = 'chart_'; NETDATA.alarms.chart_div_animation_duration = 0; @@ -2337,7 +2399,7 @@ // download all the charts the server knows NETDATA.chartRegistry.downloadAll(netdata_url, function(data) { - if(data != null) { + if(data !== null) { if(typeof data.custom_info !== 'undefined' && data.custom_info !== "") { loadJs(data.custom_info, function () { $.extend(true, netdataDashboard, customDashboard); @@ -2362,7 +2424,7 @@ var s = options.version.split('-'); if(s.length !== 3) return null; - if(s[2][0] == 'g') { + if(s[2][0] === 'g') { var v = s[2].split('_')[0].substring(1, 8); if(v.length === 7) { versionLog('Installed git commit id of netdata is ' + v); @@ -2566,17 +2628,17 @@ var tagName = null; for (var i = 0, r = 0; r <= config.showChars; i++) { - if (content[i] == '<' && !inTag) { + if (content[i] === '<' && !inTag) { inTag = true; // This could be "tag" or "/tag" tagName = content.substring(i + 1, content.indexOf('>', i)); // If its a closing tag - if (tagName[0] == '/') { + if (tagName[0] === '/') { - if (tagName != '/' + openTags[0]) { + if (tagName !== ('/' + openTags[0])) { config.errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes'; } else { openTags.shift(); // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!) @@ -2584,12 +2646,12 @@ } else { // There are some nasty tags that don't have a close tag like
    - if (tagName.toLowerCase() != 'br') { + if (tagName.toLowerCase() !== 'br') { openTags.unshift(tagName); // Add to start the name of the tag that opens } } } - if (inTag && content[i] == '>') { + if (inTag && content[i] === '>') { inTag = false; } @@ -2668,17 +2730,21 @@ }); /* activate bootstrap scrollspy (needed for sidebar) */ + var scrollspyOffset = $(window).height() / 5; + if(scrollspyOffset > 200) scrollspyOffset = 200; + if(scrollspyOffset < 50) scrollspyOffset = 50; $(document.body).scrollspy({ target: '#sidebar', - offset: $(window).height() / 5 // controls the diff of the element to the top, to select it + offset: scrollspyOffset // controls the diff of the element to the top, to select it }); // change the URL based on the current position of the screen $sidebar.on('activate.bs.scrollspy', function (e) { - // console.log(e); + //console.log(e); var el = $(e.target); - //if(el.find('ul').size() == 0) { + //if(el.find('ul').size() === 0) { var hash = el.find('a').attr('href'); + // console.log(hash); if(typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) { urlOptions.hash = hash; //console.log(urlOptions.hash); @@ -2895,7 +2961,7 @@ }); NETDATA.requiredJs.push({ - url: NETDATA.serverDefault + 'dashboard_info.js?v20170308-1', + url: NETDATA.serverDefault + 'dashboard_info.js?v20170530-1', async: false, isAlreadyLoaded: function() { return false; } }); @@ -2993,7 +3059,7 @@ Hover on them too!
    -
    +
    @@ -3523,4 +3589,4 @@ - + diff --git a/web/infographic.html b/web/infographic.html new file mode 100644 index 000000000..0bb571870 --- /dev/null +++ b/web/infographic.html @@ -0,0 +1,170 @@ + + + + + NetData: Get control of your Linux Servers. Simple. Effective. Awesome. + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +

    + Interactive infographic of netdata features and functions +

    +

    + Hover and click on the infographic, to open the related wiki page. +
    + + The links and the docs are still a work in progress. + The interactive infographic is a feature of draw.io. + +

    +
    + +
    +

    + New to netdata? Have a look at a netdata demo. You will love it! +

    +

    + + + +

    +
    +
    +
    +
    +
    +
    + + + + + + + + + +
    + + + + + + diff --git a/web/version.txt b/web/version.txt index a7ffee85b..4c72a5b03 100644 --- a/web/version.txt +++ b/web/version.txt @@ -1 +1 @@ -f5fa346a188e906a8f2cce3c2cf32a88ce81c666 +4016e2d9e3c2fcf5f6d59827bf5f81083d6645ba -- cgit v1.2.3
    + ${chart} Chart
    + ${alarm}${info_html} Alarm
    + ${family} Family
    + ${severity} Severity
    ${date} + ${date} ${raised_for_html} Time
    + View Netdata
    The source of this alarm is line ${src} + The source of this alarm is line ${src}
    (alarms are configurable, edit this file to adapt the alarm to your needs)
    Sent by - netdata, the real-time performance monitoring. + Sent by + netdata, the real-time performance and health monitoring, on ${this_host}.